Source · range · width
Read all three before following a play. SOURCE and ATR basis remain separately inspectable.
Read the pivot map and session context. Follow each confirmed episode with its original entry, stop and target.
Seven visible levels · fourteen named plays · full source · no signup
Camarilla Desk is a free TradingView indicator that builds a frozen session map from completed price data and tracks fourteen play hypotheses. It separates the source used for levels from the session clock used for decisions. Qualifying episodes receive fixed entry, stop and target references; these observations are not confirmed order fills.
The default map shows H6, H4, H3, previous close, L3, L4 and L6. Levels come from the selected completed source and freeze at the decision opening; they are Camarilla references, not floor pivots or CPR. See the map formulas
The map needs complete source coverage, a valid ATR basis and a known decision opening. Missing boundaries, stale sources or incomplete sessions can block interpretation. Check the resolved source and clock before treating a waiting row as a setup. Check source and session coverage
No. H names the Higher context, while SIDE identifies the direction of the hypothesis. Some Higher plays point short. Read the opening gate, approach region and confirmation rule together; the context label alone does not define an entry. Read the common play questions
Camarilla starts with earlier prices and calculates places to watch. This desk explains how price behaves at those places, then checks whether a named play qualifies. First, see where the lines come from.
The map comes from an earlier trading period. Here, its candles establish a high and low. While that period is still open, its final close is not known.
Change the close and wick. See the rule respond while the Camarilla map stays still.
Illustrated prices · four closed candles · fixed scale while you drag.
Opening, source and ATR edits restart this hypothetical replay. The earlier range is centred on its close. Source completeness and the ATR scalar are supplied assumptions: this lesson does not reconstruct a full session feed, ATR seed or missing-session recovery.
ATR Market default: observed RTH for futures unless the decision clock is Exchange day; Provider daily otherwise. This completed ATR(14) is supplied, not calculated or matched to Premium.
Candidate passes context, width and inclusive opening gates without a bad-OPEN latch. Approaching adds the strict price region. Neither is ACTIVE. Similar includes the two-percent width tolerance; ATR zero is unavailable. This lesson fixes structural risk, SMA off and one episode per session.
Explore HA/HB/HE here; all fourteen rules appear below. Recovery accepts either candle-body direction. Stops round outward and targets toward entry. Outcomes begin after activation, with opening gaps then stop-first touches. No broker fill is simulated.
Illustrative inputs · changing a slider recalculates this example · no market feed
Read each opening gate, setup, confirming close and ending.
Default Top Left Ribbon: source, range, width. Trading Dark keeps seven levels visible. Open HUD or Inspector for detail.
Read all three before following a play. SOURCE and ATR basis remain separately inspectable.
Entry arrows, shaded reference regions and named outcomes identify the episode. Ended geometry stops at its recorded end, not a new entry.
Inspect any code’s prerequisites and NEXT. Manual selection cannot switch the tracked plan. Candidate, approach, armed and active remain distinct.
The detailed HUD dates the committed close and map. Display changes cannot change qualification or reference prices.


No signup; complete MIT sources. Use the installation walkthrough. Save each source separately.
Seven-level map, context ribbon, explicit ATR basis and frozen reference episodes.
v2.1.0 · MIT · SHA256 93e60db80ed8a14c475e5812404a0f50016edae741d122202b33db86b620664b
//@version=6
// CAMARILLA DESK 2.1.0 · MIT · Original interpretation of public Camarilla concepts.
// CARD: market/timeframe — standard time-based intraday bars, 1m through 1h.
// CARD: idea — confirmed play states, frozen prior-source map and reference plans.
// ACTIVE denotes an observed hypothesis, not a filled order or a validated edge.
// See docs/engine-contract.md for exact rules and docs/build-report.md for evidence.
indicator("Camarilla Desk · Open Source", "Cam Desk", overlay = true, behind_chart = false, max_lines_count = 220, max_labels_count = 160, max_boxes_count = 50, max_bars_back = 3000)
const string CAM_VERSION = "2.1.0"
const bool CAM_IS_LAB = false
// PUBLIC LITE BASELINE PREREQUISITES · original closed-bar execution · no Premium parity.
string CAM_FORMULA = "expanded6-v1"
string camSourceGroup = "Source and decision clock"
string sourceMode = input.string("Market default", "Pivot source", options = ["Market default", "Auto RTH/ETH", "RTH", "ETH", "Exchange daily", "Selected session"], group = camSourceGroup, display = display.none, tooltip = "Market default explicitly chooses RTH for non-crypto and Exchange daily for crypto. RTH/ETH require complete observed one-minute windows. Optional Auto RTH/ETH requires complete pre/postmarket coverage and freezes at the opening; it never silently falls back. Exchange daily remains a provider-defined daily/settlement source, never cash RTH.")
string decisionMode = input.string("Auto", "Decision window", options = ["Auto", "US cash", "Exchange day", "Custom"], group = camSourceGroup, display = display.none, tooltip = "Auto uses Exchange day for crypto and US cash 09:30–16:00 New York for other assets. Set Custom for another exchange or a futures cash window.")
string customSession = input.session("0930-1600", "Custom decision interval", group = camSourceGroup, active = decisionMode == "Custom", display = display.none)
string customTimezone = input.string("America/New_York", "Custom IANA time zone", group = camSourceGroup, active = decisionMode == "Custom", display = display.none, tooltip = "Use an IANA region such as America/New_York. True overnight intervals use the ending trading weekday. Equal start/end is 24 hours and uses the starting calendar weekday.")
bool customWeekdays = input.bool(true, "Custom weekdays only", group = camSourceGroup, active = decisionMode == "Custom", display = display.none)
string regularSession = input.session("0930-1600", "RTH source interval", group = camSourceGroup, display = display.none)
string regularTimezone = input.string("America/New_York", "RTH source IANA zone", group = camSourceGroup, display = display.none, tooltip = "Cash-session definition, including futures emulation. It is independent from the exchange's definition of regular trading.")
string extendedClock = input.string("Auto", "ETH source clock", options = ["Auto", "Custom"], group = camSourceGroup, display = display.none, tooltip = "Auto: US equities 04:00–20:00 New York, futures 18:00–17:00 New York, crypto exchange day. Other venues require a Custom source clock.")
string extendedSession = input.session("1800-1700", "Custom ETH interval", group = camSourceGroup, active = extendedClock == "Custom", display = display.none)
string extendedTimezone = input.string("America/New_York", "Custom ETH IANA zone", group = camSourceGroup, active = extendedClock == "Custom", display = display.none)
string closeMode = input.string("Match source", "Pivot close selection", options = ["Match source", "RTH", "ETH", "Exchange daily"], group = camSourceGroup, display = display.none)
float switchingTolerance = input.float(2, "Auto excursion · % selected ATR", minval = 0, maxval = 100, step = 0.1, group = camSourceGroup, active = sourceMode == "Auto RTH/ETH", display = display.none)
string camPrerequisiteGroup = "Public Lite baseline · confirmed prerequisites"
float rangeThresholdPct = input.float(2, "Neutral close change · % selected ATR", minval = 0, maxval = 100, step = 0.1, group = camPrerequisiteGroup, display = display.none)
float widthThresholdPct = input.float(2, "Similar width tolerance · %", minval = 0, maxval = 99, step = 0.1, group = camPrerequisiteGroup, display = display.none)
bool allowNeutral = input.bool(false, "Allow both directions in Neutral", group = camPrerequisiteGroup, display = display.none)
bool smaGate = input.bool(false, "Original SMA close confirmation", group = camPrerequisiteGroup, display = display.none, tooltip = "Optional original filter: confirmed long closes above the chart SMA, short below. This does not reproduce an undisclosed Premium SMA trigger.")
int smaLength = input.int(20, "Chart SMA length", minval = 2, maxval = 300, group = camPrerequisiteGroup, active = smaGate, display = display.none)
string camPlayGroup = "Original structural hypotheses"
bool enableA = input.bool(true, "A · Third-band traverse", group = camPlayGroup, display = display.none)
bool enableB = input.bool(true, "B · Interior breakout and retest", group = camPlayGroup, display = display.none)
bool enableC = input.bool(true, "C · Opening continuation", group = camPlayGroup, display = display.none)
bool enableD = input.bool(true, "D · Context-opposed continuation", group = camPlayGroup, display = display.none)
bool enableE = input.bool(true, "E · Fourth-tier failure", group = camPlayGroup, display = display.none)
bool enableF = input.bool(true, "F · Outer-tier rejection", group = camPlayGroup, display = display.none)
bool enableG = input.bool(false, "G · Opposite-side outer rejection", group = camPlayGroup, display = display.none)
int setupDeadline = input.int(12, "Setup deadline · completed bars", minval = 1, maxval = 200, group = camPlayGroup, display = display.none, tooltip = "Confirmation is allowed through age 12 at the default. The following closed bar expires a pending setup.")
int activeDeadline = input.int(120, "Active deadline · completed bars", minval = 1, maxval = 1000, group = camPlayGroup, display = display.none, tooltip = "Observed later-bar outcomes precede timeout. Default timeout occurs on the 120th later completed chart bar.")
float minRewardR = input.float(1.0, "Minimum structural reward / risk", minval = 0.1, maxval = 10, step = 0.1, group = camPlayGroup, display = display.none)
int stopBufferTicks = input.int(1, "Stop buffer · minimum ticks", minval = 0, maxval = 100, group = camPlayGroup, display = display.none)
string stopModel = input.string("Structural", "Reference stop model", options = ["Structural", "ATR"], group = camPlayGroup, display = display.none, tooltip = "ATR is an original alternative measured from activation close using the frozen selected ATR basis. Neither option claims a vendor preset.")
float stopAtrMultiple = input.float(0.25, "ATR stop multiple", minval = 0.01, maxval = 10, step = 0.05, group = camPlayGroup, active = stopModel == "ATR", display = display.none)
string targetModel = input.string("Structural", "Reference target model", options = ["Structural", "ATR"], group = camPlayGroup, display = display.none)
float targetAtrMultiple = input.float(0.5, "ATR target multiple", minval = 0.01, maxval = 20, step = 0.05, group = camPlayGroup, active = targetModel == "ATR", display = display.none)
float maxTargetAtr = input.float(0, "Maximum target distance · selected ATR (0 off)", minval = 0, maxval = 20, step = 0.1, group = camPlayGroup, display = display.none, tooltip = "Optional original reachability guard; rejects a too-distant target without silently moving it.")
bool allowRearm = input.bool(false, "Allow another episode after ending", group = camPlayGroup, display = display.none)
int cooldownBars = input.int(3, "Rearm cooldown · completed bars", minval = 1, maxval = 200, group = camPlayGroup, active = allowRearm, display = display.none)
int maxEpisodes = input.int(1, "Maximum episodes per code / session", minval = 1, maxval = 10, group = camPlayGroup, active = allowRearm, display = display.none)
string camAlertGroup = "Aggregate JSON alerts"
bool alertActivation = input.bool(true, "Include activation events", group = camAlertGroup, display = display.none)
bool alertEnding = input.bool(true, "Include ending events", group = camAlertGroup, display = display.none)
bool alertArmed = input.bool(false, "Include armed events", group = camAlertGroup, display = display.none)
// Appended to preserve the ordering of all previous core inputs.
string atrBasisMode = input.string("Market default", "ATR calculation basis", options = ["Market default", "Provider daily", "RTH session"], group = camSourceGroup, display = display.none, tooltip = "Market default uses completed observed RTH-session Wilder ATR(14) for futures with a non-Exchange-day decision clock, and completed provider daily ATR(14) otherwise. RTH uses the configured RTH interval and zone; seed = mean of 14 valid session true ranges, then Wilder smoothing. An observed incomplete/malformed session resets the seed. Entirely absent days are skipped: this is observed-session coverage, not an exchange holiday calendar. No fallback. The chosen basis is frozen at the decision opening and drives range, Auto excursion and optional ATR risk/target checks.")
type CamLevels
bool valid = false
bool outerValid = false
bool complete = false
float h = na
float l = na
float c = na
float h2 = na
float l2 = na
float h3 = na
float l3 = na
float h4 = na
float l4 = na
float h5 = na
float l5 = na
float h6 = na
float l6 = na
int sourceStart = na
int sourceEnd = na
string reason = "Source unavailable"
type CamWindowBook
CamLevels latest
CamLevels prior
int start = na
int finish = na
int lastClose = na
float h = na
float l = na
float c = na
int samples = 0
bool complete = false
bool finished = true
string reason = "No observed interval"
// Constant-size Wilder state, updated only when an observed RTH window finishes.
type CamAtrBook
int samples = 0
float seedSum = 0
float previousClose = na
float value = na
int sourceStart = na
int sourceEnd = na
int observedEnd = na
type CamSourcePacket
CamLevels current
CamLevels prior
bool ready = false
bool openKnown = false
float opening = na
int openingTime = na
int sessionStart = na
int sessionEnd = na
int cutoff = na
string clockLabel = ""
string sourceKind = ""
string closeKind = ""
int closeSourceEnd = na
int priorCloseSourceEnd = na
float atr = na
int atrStamp = na
int atrBasisCode = 0
int atrSamples = 0
int atrStart = na
bool atrReady = false
float excursion = na
float excursionThreshold = na
string reason = "Waiting for an observed opening in the 1m source context"
string selectionReason = ""
string coverage = ""
int sourceCode = 0
int closeCode = 0
int reasonCode = 0
int selectionCode = 0
int rthSamples = 0
int ethSamples = 0
int selectedSamples = 0
bool rthComplete = false
bool ethComplete = false
bool selectedComplete = false
type Play
string code = ""
string name = ""
string family = ""
int biasRequired = 0
int dir = 0
string state = "DATA WAIT"
string reason = "Waiting for a decision session"
string next = "Wait for a fully observed opening"
string invalidation = "No setup observed"
bool eligible = false
bool candidate = false
bool approaching = false
bool rangeOk = false
bool widthOk = false
bool openOk = false
bool regionOk = false
bool blocked = false
bool smaOk = true
int episode = 0
float triggerPrice = na
string triggerOperator = ""
string sourceKind = ""
string closeKind = ""
string stopMode = "Structural"
string targetMode = "Structural"
float atrUsed = na
int atrStamp = na
string atrBasis = "Unavailable"
int atrSamples = 0
int atrStart = na
int sourceCutoff = na
int closeSourceEnd = na
float level = na
int setupBar = na
int setupTime = na
float armedHigh = na
float armedLow = na
int activationBar = na
int activationTime = na
int activationSeq = na
float entry = na
float stop = na
float target = na
float effectiveR = na
int sessionStart = na
int sourceStart = na
int sourceEnd = na
int endBar = na
int endTime = na
float outcomePrice = na
bool ambiguous = false
type CamPlanSnapshot
string code = ""
string name = ""
int dir = 0
int episode = 0
int sessionStart = na
int sourceStart = na
int sourceEnd = na
string sourceKind = ""
string closeKind = ""
int setupTime = na
float triggerPrice = na
string triggerOperator = ""
float atrUsed = na
string stopMode = ""
string targetMode = ""
int activationBar = na
int activationTime = na
float entry = na
float stop = na
float target = na
float effectiveR = na
int endBar = na
int endTime = na
float outcomePrice = na
string state = ""
string reason = ""
bool ambiguous = false
string atrBasis = "Unavailable"
int atrSamples = 0
int atrStart = na
int atrStamp = na
type CamEvent
int seq = 0
string code = ""
string kind = ""
string reason = ""
int dir = 0
int eventTime = na
int sessionStart = na
int sourceStart = na
int sourceEnd = na
float price = na
float entry = na
float stop = na
float target = na
bool ambiguous = false
int episode = 0
string sourceKind = ""
string closeKind = ""
int atrStamp = na
int sourceCutoff = na
int closeSourceEnd = na
float atrUsed = na
string atrBasis = "Unavailable"
int atrSamples = 0
int atrStart = na
var array<Play> plays = array.new<Play>()
var array<CamPlanSnapshot> camPlans = array.new<CamPlanSnapshot>()
var array<CamEvent> camEvents = array.new<CamEvent>()
var array<CamEvent> camBarEvents = array.new<CamEvent>()
var array<int> camEventCounter = array.new_int(1, 0)
// Comparison normalization is relative to the actual operand scale, never to 1.
// Self-normalized sign avoids Pine comparison operators' nine-decimal rounding.
f_camSign(float value) =>
na(value) ? 0 : int(nz(value / math.abs(value), 0))
f_camCompare(float first, float rhs) =>
float difference = first - rhs
float tolerance = 4.0 * 2.220446049250313e-16 * math.max(math.abs(first), math.abs(rhs))
na(difference) ? 0 : f_camSign(math.abs(difference) - tolerance) <= 0 ? 0 : f_camSign(difference)
f_camNumber(float value) =>
string result = "null"
if not na(value)
if f_camSign(value) == 0
result := "0"
else
int exponent = int(math.floor(math.log10(math.abs(value))))
result := exponent < -6 or exponent > 12 ? str.tostring(value / math.pow(10, exponent), "0.################") + "e" + str.tostring(exponent) : str.tostring(value, "0.################")
result
f_camPrice(float value) =>
na(value) ? "—" : str.tostring(value, format.mintick)
f_camExact(float value) =>
na(value) ? "—" : f_camNumber(value)
// Narrative rounding suppresses binary float tails; raw map/plan values stay unchanged.
f_camReadable(float value) =>
na(value) ? "—" : f_camCompare(math.abs(value), 0.000001) < 0 ? f_camNumber(value) : str.tostring(value, "0.##########")
f_camTerminal(string state) =>
state == "REJECTED" or state == "INVALIDATED" or state == "TARGET" or state == "STOP" or state == "EXPIRED"
f_camTouched(float level, float barHigh, float barLow) =>
not na(level) and not na(barLow) and not na(barHigh) and f_camCompare(barLow, level) <= 0 and f_camCompare(barHigh, level) >= 0
// A flat positive bar is valid. Missing or incoherent constituent prices are not.
f_camBarValid(float barOpen, float barHigh, float barLow, float barClose) =>
not na(barOpen) and not na(barHigh) and not na(barLow) and not na(barClose) and f_camSign(barLow) > 0 and f_camCompare(barHigh, math.max(barOpen, barClose)) >= 0 and f_camCompare(barLow, math.min(barOpen, barClose)) <= 0
f_camLevels(float srcHigh, float srcLow, float srcClose, int srcStart, int srcEnd, bool complete, string incompleteReason, bool requireCloseInside = true) =>
CamLevels m = CamLevels.new()
m.h := srcHigh
m.l := srcLow
m.c := srcClose
m.sourceStart := srcStart
m.sourceEnd := srcEnd
bool independentValid = not na(srcHigh) and not na(srcLow) and not na(srcClose) and f_camSign(srcLow) > 0 and f_camSign(srcClose) > 0 and f_camCompare(srcHigh, srcLow) >= 0
m.complete := complete and (requireCloseInside ? f_camBarValid(srcClose, srcHigh, srcLow, srcClose) : independentValid)
bool coherent = m.complete and f_camCompare(srcHigh, srcLow) > 0
if complete and coherent
float width = srcHigh - srcLow
m.h2 := srcClose + 1.1 * width / 6
m.l2 := srcClose - 1.1 * width / 6
m.h3 := srcClose + 1.1 * width / 4
m.l3 := srcClose - 1.1 * width / 4
m.h4 := srcClose + 1.1 * width / 2
m.l4 := srcClose - 1.1 * width / 2
m.h5 := m.h4 + 1.168 * (m.h4 - m.h3)
m.l5 := m.l4 - 1.168 * (m.l3 - m.l4)
m.h6 := srcHigh / srcLow * srcClose
m.l6 := 2 * srcClose - m.h6
m.valid := f_camCompare(m.h4, m.h3) > 0 and f_camCompare(m.h3, m.l3) > 0 and f_camCompare(m.l3, m.l4) > 0
m.outerValid := m.valid and not na(m.h6) and not na(m.l6) and f_camSign(m.l6) > 0 and f_camCompare(m.h6, m.h4) > 0 and f_camCompare(m.l6, m.l4) < 0
m.reason := not m.valid ? "Collapsed inner geometry" : not m.outerValid ? "Outer ratio geometry unavailable" : "Complete coherent source"
else
m.reason := not complete ? incompleteReason : "Missing, nonpositive or incoherent source H/L/C"
m
f_camRelationship(CamLevels current, CamLevels previous) =>
string relation = "Unavailable"
int bias = 0
float ratio = na
string widthLabel = "Unavailable"
if current.valid and previous.valid
float a = previous.l3
float b = previous.h3
float c = current.l3
float d = current.h3
int lowerCompare = f_camCompare(c, a)
int upperCompare = f_camCompare(d, b)
bias := lowerCompare > 0 and upperCompare > 0 ? 1 : lowerCompare < 0 and upperCompare < 0 ? -1 : 0
relation := lowerCompare == 0 and upperCompare == 0 ? "Unchanged" : f_camCompare(c, b) > 0 ? "Higher separated" : f_camCompare(d, a) < 0 ? "Lower separated" : lowerCompare >= 0 and upperCompare <= 0 ? "Inside band" : lowerCompare <= 0 and upperCompare >= 0 ? "Outside band" : lowerCompare > 0 and upperCompare > 0 ? "Overlapping higher" : "Overlapping lower"
ratio := (d - c) / (b - a)
int widthCompare = f_camCompare(d + a, c + b)
widthLabel := widthCompare > 0 ? "Wide" : widthCompare < 0 ? "Narrow" : "Equal"
[relation, bias, ratio, widthLabel]
// Independent transcription of public Lite 2.0 prerequisites, applied on closed bars.
// Original episode triggers below are a separate layer; these flags are not entries.
f_camPrerequisites(string code, CamLevels m, CamLevels prior, float atr, float opening, float barOpen, float barClose, bool priorBlocked, float rangePct, float widthPct, bool neutralAllowed) =>
string family = str.substring(code, 1, 2)
bool higher = str.substring(code, 0, 1) == "H"
bool innerReady = m.valid and prior.valid
bool atrReady = not na(atr) and f_camSign(atr) > 0
bool observationReady = not na(opening) and not na(barOpen) and not na(barClose)
float threshold = atr * rangePct / 100
float delta = m.c - prior.c
int bias = innerReady and atrReady ? (f_camCompare(m.c, prior.c + threshold) > 0 ? 1 : f_camCompare(m.c, prior.c - threshold) < 0 ? -1 : 0) : 0
float width = m.h3 - m.l3
float previousWidth = prior.h3 - prior.l3
float ratio = innerReady and f_camSign(previousWidth) > 0 ? width / previousWidth : na
// Cross-sums retain the original endpoint scale when subtracting nearby prices.
float upperFactor = 1 + widthPct / 100
float lowerFactor = 1 - widthPct / 100
string widthLabel = na(ratio) ? "Unavailable" : f_camCompare(m.h3 + prior.l3 * upperFactor, m.l3 + prior.h3 * upperFactor) > 0 ? "Wide" : f_camCompare(m.h3 + prior.l3 * lowerFactor, m.l3 + prior.h3 * lowerFactor) < 0 ? "Narrow" : "Similar"
bool rangeOk = innerReady and atrReady and (bias == (higher ? 1 : -1) or bias == 0 and neutralAllowed)
bool widthOk = not na(ratio) and (family == "F" or family == "G" or widthLabel == "Similar" or (family == "A" or family == "E" ? widthLabel == "Wide" : widthLabel == "Narrow"))
float openLow = family == "A" ? m.l2 : family == "B" or family == "E" ? (higher ? m.l2 : m.l3) : family == "G" ? (higher ? m.l6 : m.c) : family == "D" ? (higher ? m.l6 : m.h4) : (higher ? m.h4 : m.l6)
float openHigh = family == "A" ? m.h2 : family == "B" or family == "E" ? (higher ? m.h3 : m.h2) : family == "G" ? (higher ? m.c : m.h6) : family == "D" ? (higher ? m.l4 : m.h6) : (higher ? m.h6 : m.l4)
bool openOk = observationReady and f_camCompare(opening, openLow) >= 0 and f_camCompare(opening, openHigh) <= 0
bool upperSide = higher != (family == "D" or family == "G")
bool regionOk = observationReady and (family == "A" ? (higher ? f_camCompare(barClose, m.l4) > 0 and f_camCompare(barClose, m.c) < 0 : f_camCompare(barClose, m.h4) < 0 and f_camCompare(barClose, m.c) > 0) : family == "B" or family == "E" ? (higher ? f_camCompare(barClose, m.h3) > 0 and f_camCompare(barClose, m.h6) < 0 : f_camCompare(barClose, m.l3) < 0 and f_camCompare(barClose, m.l6) > 0) : family == "C" or family == "D" ? (upperSide ? f_camCompare(barClose, m.h3) > 0 : f_camCompare(barClose, m.l3) < 0) : (upperSide ? f_camCompare(barClose, m.h4) > 0 : f_camCompare(barClose, m.l4) < 0))
float badLow = family == "A" ? (higher ? m.l4 : m.l2) : family == "B" or family == "E" ? (higher ? m.l2 : m.l6) : upperSide ? (family == "G" ? m.c : m.h3) : m.l6 - (m.l4 - m.l6) * 0.5
float badHigh = family == "A" ? (higher ? m.h2 : m.h4) : family == "B" or family == "E" ? (higher ? m.h6 : m.h2) : upperSide ? m.h6 + (m.h6 - m.h4) * 0.5 : (family == "G" ? m.c : m.l3)
bool blocked = priorBlocked or (observationReady and (f_camCompare(barOpen, badLow) < 0 or f_camCompare(barOpen, badHigh) > 0))
bool geometryReady = innerReady and (family == "A" or m.outerValid)
bool candidate = geometryReady and atrReady and observationReady and rangeOk and widthOk and openOk and not blocked
bool approaching = candidate and regionOk
[bias, ratio, widthLabel, rangeOk, widthOk, openOk, regionOk, blocked, candidate, approaching]
f_camCodeIndex(string code) =>
int found = -1
if array.size(plays) > 0
for i = 0 to array.size(plays) - 1
if array.get(plays, i).code == code
found := i
found
f_camAutoFocus() =>
int found = -1
int latest = na
if array.size(plays) > 0
for i = 0 to array.size(plays) - 1
Play p = array.get(plays, i)
if p.state == "ACTIVE" and (na(latest) or p.activationTime > latest)
latest := p.activationTime
found := i
found
f_camStorePlan(Play p) =>
if not na(p.activationTime)
CamPlanSnapshot snapshot = CamPlanSnapshot.new(p.code, p.name, p.dir, p.episode, p.sessionStart, p.sourceStart, p.sourceEnd, p.sourceKind, p.closeKind, p.setupTime, p.triggerPrice, p.triggerOperator, p.atrUsed, p.stopMode, p.targetMode, p.activationBar, p.activationTime, p.entry, p.stop, p.target, p.effectiveR, p.endBar, p.endTime, p.outcomePrice, p.state, p.reason, p.ambiguous)
snapshot.atrBasis := p.atrBasis
snapshot.atrSamples := p.atrSamples
snapshot.atrStart := p.atrStart
snapshot.atrStamp := p.atrStamp
int found = -1
if array.size(camPlans) > 0
for i = 0 to array.size(camPlans) - 1
CamPlanSnapshot old = array.get(camPlans, i)
if old.code == p.code and old.sessionStart == p.sessionStart and old.episode == p.episode
found := i
break
if found >= 0
array.remove(camPlans, found)
array.unshift(camPlans, snapshot)
if array.size(camPlans) > 40
// Never evict an ongoing plan merely because other codes re-entered.
int oldestEnded = -1
for i = array.size(camPlans) - 1 to 0
if array.get(camPlans, i).state != "ACTIVE"
oldestEnded := i
break
if oldestEnded >= 0
array.remove(camPlans, oldestEnded)
p
f_camEmit(Play p, string kind, string reason, float price, int stamp, bool ambiguous) =>
int seq = array.get(camEventCounter, 0) + 1
array.set(camEventCounter, 0, seq)
CamEvent e = CamEvent.new(seq, p.code, kind, reason, p.dir, stamp, p.sessionStart, p.sourceStart, p.sourceEnd, price, p.entry, p.stop, p.target, ambiguous)
e.episode := p.episode
e.sourceKind := p.sourceKind
e.closeKind := p.closeKind
e.atrStamp := p.atrStamp
e.atrUsed := p.atrUsed
e.atrBasis := p.atrBasis
e.atrSamples := p.atrSamples
e.atrStart := p.atrStart
e.sourceCutoff := p.sourceCutoff
e.closeSourceEnd := p.closeSourceEnd
f_camStorePlan(p)
array.unshift(camEvents, e)
array.push(camBarEvents, e)
if array.size(camEvents) > 60
array.pop(camEvents)
seq
f_camArm(Play p, float barHigh, float barLow, int bar, int stamp, string reason) =>
p.episode += 1
p.state := "ARMED"
p.setupBar := bar
p.setupTime := stamp
p.armedHigh := barHigh
p.armedLow := barLow
p.reason := reason
f_camEmit(p, "ARMED", reason, p.level, stamp, false)
p
f_camEnd(Play p, string kind, string reason, float price, int bar, int stamp, bool ambiguous) =>
p.state := kind
p.reason := reason
p.next := "Episode ended · next dated session"
p.endBar := bar
p.endTime := stamp
p.outcomePrice := price
p.ambiguous := ambiguous
f_camEmit(p, kind, reason, price, stamp, ambiguous)
p
// Normalize only machine-noise near a whole tick; retain actual sub-tick prices.
f_camDistance(float first, float rhs, float tick) =>
float raw = math.abs(first - rhs)
float ticks = raw / tick
float nearest = math.round(ticks)
float noise = 8.0 * 2.220446049250313e-16 * math.max(math.max(math.abs(first), math.abs(rhs)), tick) / tick
f_camSign(math.abs(ticks - nearest) - math.min(noise, 0.000001)) <= 0 ? nearest * tick : raw
f_camGrid(float value, float tick, bool upward) =>
float ticks = value / tick
float nearest = math.round(ticks)
float tolerance = math.min(8.0 * 2.220446049250313e-16 * math.max(math.abs(ticks), 1), 0.000001)
float stable = f_camSign(math.abs(ticks - nearest) - tolerance) <= 0 ? nearest : ticks
(upward ? math.ceil(stable) : math.floor(stable)) * tick
f_camPlan(int dir, string family, CamLevels m, float armedHigh, float armedLow, float entry, bool wide, float tick, int bufferTicks, float minimumR) =>
float boundary = na
float destination = na
if family == "A"
boundary := dir == 1 ? math.min(m.l4, armedLow) : math.max(m.h4, armedHigh)
destination := dir == 1 ? m.h3 : m.l3
else if family == "B" or family == "C" or family == "D"
boundary := dir == 1 ? m.h3 : m.l3
destination := dir == 1 ? m.h6 : m.l6
else if family == "E"
boundary := dir == 1 ? math.min(m.l6, armedLow) : math.max(m.h6, armedHigh)
destination := dir == 1 ? m.h4 : m.l4
else
boundary := dir == 1 ? math.min(armedLow, m.l6 - 0.25 * (m.l3 - m.l4)) : math.max(armedHigh, m.h6 + 0.25 * (m.h4 - m.h3))
destination := wide ? (dir == 1 ? m.l4 : m.h4) : m.c
float stopRaw = boundary - dir * bufferTicks * tick
float stop = f_camGrid(stopRaw, tick, dir == -1)
float target = f_camGrid(destination, tick, dir == -1)
bool positive = not na(entry) and not na(stop) and not na(target) and f_camSign(entry) > 0 and f_camSign(stop) > 0 and f_camSign(target) > 0
bool directionOk = positive and f_camCompare(entry, stop) * dir > 0 and f_camCompare(target, entry) * dir > 0
float risk = directionOk ? f_camDistance(entry, stop, tick) : na
float reward = directionOk ? f_camDistance(entry, target, tick) : na
float effectiveR = directionOk and f_camSign(risk) > 0 ? reward / risk : na
bool valid = directionOk and f_camCompare(risk / tick, 1) >= 0 and f_camCompare(effectiveR, minimumR) >= 0
string reason = not positive ? "Nonpositive or unavailable plan prices" : not directionOk ? "Destination or stop is on the wrong side of entry" : f_camCompare(risk / tick, 1) < 0 ? "Risk distance is smaller than one tick" : f_camCompare(effectiveR, minimumR) < 0 ? "Structural R " + f_camExact(effectiveR) + " < minimum " + f_camExact(minimumR) : "Confirmed structural hypothesis · reference plan only"
[valid, stop, target, effectiveR, reason]
f_camPlanOptions(int dir, string family, CamLevels m, float armedHigh, float armedLow, float entry, bool wide, float tick, int bufferTicks, float minimumR, float atr, string stopChoice, float stopMultiple, string targetChoice, float targetMultiple, float reachLimit) =>
[structuralOk, structuralStop, structuralTarget, structuralR, structuralReason] = f_camPlan(dir, family, m, armedHigh, armedLow, entry, wide, tick, bufferTicks, minimumR)
bool atrReady = not na(atr) and f_camSign(atr) > 0
float stop = stopChoice == "ATR" and atrReady ? f_camGrid(entry - dir * (atr * stopMultiple + bufferTicks * tick), tick, dir == -1) : structuralStop
float target = targetChoice == "ATR" and atrReady ? f_camGrid(entry + dir * atr * targetMultiple, tick, dir == -1) : structuralTarget
bool positive = not na(stop) and not na(target) and f_camSign(entry) > 0 and f_camSign(stop) > 0 and f_camSign(target) > 0
bool correctSide = positive and f_camCompare(entry, stop) * dir > 0 and f_camCompare(target, entry) * dir > 0
float risk = correctSide ? f_camDistance(entry, stop, tick) : na
float reward = correctSide ? f_camDistance(entry, target, tick) : na
float rr = correctSide and f_camSign(risk) > 0 ? reward / risk : na
bool enoughRisk = correctSide and f_camCompare(risk / tick, 1) >= 0
bool enoughReward = enoughRisk and f_camCompare(rr, minimumR) >= 0
bool reachable = reachLimit == 0 or atrReady and f_camCompare(reward, atr * reachLimit) <= 0
bool valid = atrReady and enoughReward and reachable
string reason = not atrReady ? "Confirmed selected ATR unavailable" : not positive ? "Nonpositive or unavailable reference prices" : not correctSide ? "Stop or target is on the wrong side" : not enoughRisk ? "Risk is smaller than one tick" : not enoughReward ? "Reference R " + f_camExact(rr) + " < minimum " + f_camExact(minimumR) : not reachable ? "Target exceeds " + f_camExact(reachLimit) + " selected ATR reach limit" : "Original confirmed reference geometry"
[valid, stop, target, rr, reason]
f_camOutcome(Play p, float barOpen, float barHigh, float barLow, int bar, int stamp, bool contained, int timeout, bool sessionEnded) =>
string kind = ""
string reason = ""
float price = na
bool ambiguous = false
if p.state == "ACTIVE" and bar > p.activationBar
if contained and f_camBarValid(barOpen, barHigh, barLow, barOpen)
bool stopGap = f_camCompare(barOpen, p.stop) * p.dir <= 0
bool targetGap = f_camCompare(barOpen, p.target) * p.dir >= 0
bool stopTouch = p.dir == 1 ? f_camCompare(barLow, p.stop) <= 0 : f_camCompare(barHigh, p.stop) >= 0
bool targetTouch = p.dir == 1 ? f_camCompare(barHigh, p.target) >= 0 : f_camCompare(barLow, p.target) <= 0
if stopGap
kind := "STOP"
price := barOpen
reason := "Opening price beyond stop · reference outcome at observed open"
else if targetGap
kind := "TARGET"
price := p.target
reason := "Opening price beyond destination · fixed reference destination"
else if stopTouch
kind := "STOP"
price := p.stop
ambiguous := targetTouch
reason := targetTouch ? "Both levels touched · stop-first convention; path unknown" : "Reference stop touched on a later bar"
else if targetTouch
kind := "TARGET"
price := p.target
reason := "Reference destination touched on a later bar"
if kind == "" and bar - p.activationBar >= timeout
kind := "EXPIRED"
reason := "Completed-bar active deadline · no fill inferred"
if kind == "" and sessionEnded
kind := "EXPIRED"
reason := contained ? "Decision session ended · no fill inferred" : "Session end observed later or across a boundary · no fill inferred"
[kind, price, ambiguous, reason]
f_camFamilyEnabled(int familyIndex) =>
familyIndex == 0 ? enableA : familyIndex == 1 ? enableB : familyIndex == 2 ? enableC : familyIndex == 3 ? enableD : familyIndex == 4 ? enableE : familyIndex == 5 ? enableF : enableG
f_camNewPlay(int index, CamLevels m, bool mapReady, string dataReason, int bias, float opening, bool openingKnown, int sessionStart) =>
int familyIndex = int(math.floor(index / 2))
bool higher = index % 2 == 0
string family = familyIndex == 0 ? "A" : familyIndex == 1 ? "B" : familyIndex == 2 ? "C" : familyIndex == 3 ? "D" : familyIndex == 4 ? "E" : familyIndex == 5 ? "F" : "G"
string familyName = family == "A" ? "Traverse" : family == "B" ? "Break and retest" : family == "C" ? "Opening continuation" : family == "D" ? "Opposed continuation" : family == "E" ? "Fourth-tier failure" : family == "F" ? "Outer rejection" : "Opposite outer rejection"
Play p = Play.new()
p.code := (higher ? "H" : "L") + family
p.name := (higher ? "Higher · " : "Lower · ") + familyName
p.family := family
p.biasRequired := higher ? 1 : -1
p.dir := (family == "D" or family == "E" or family == "F") ? (higher ? -1 : 1) : (higher ? 1 : -1)
p.sessionStart := sessionStart
p.sourceStart := m.sourceStart
p.sourceEnd := m.sourceEnd
bool longSide = p.dir == 1
p.level := family == "A" ? (longSide ? m.l3 : m.h3) : family == "E" ? (longSide ? m.l4 : m.h4) : family == "F" or family == "G" ? (longSide ? m.l6 : m.h6) : (longSide ? m.h4 : m.l4)
p.triggerPrice := p.level
p.triggerOperator := longSide ? ">" : "<"
string levelText = f_camExact(p.level)
p.next := family == "B" ? "Closed break of " + levelText + "; later contained retest" : family == "E" ? "Closed excursion beyond " + levelText + "; later failure close" : "Touch " + levelText + "; close " + (longSide ? "above" : "below")
p.invalidation := family == "A" ? "Pending close " + (longSide ? "≤ " + f_camExact(m.l4) : "≥ " + f_camExact(m.h4)) : family == "B" or family == "C" or family == "D" ? "Pending close " + (longSide ? "≤ " + f_camExact(m.h3) : "≥ " + f_camExact(m.l3)) : family == "E" ? "Pending close " + (longSide ? "≤ " + f_camExact(m.l6) : "≥ " + f_camExact(m.h6)) : "Pending setup deadline; no invented farther pivot"
if not f_camFamilyEnabled(familyIndex)
p.state := "DISABLED"
p.reason := "Family " + family + " is disabled"
p.next := "Enable this family in settings"
else if not mapReady
p.state := "DATA WAIT"
p.reason := dataReason
else if not openingKnown
p.state := "DATA WAIT"
p.reason := "OPEN UNKNOWN · exact contained session opening not observed"
else if family != "A" and not m.outerValid
p.state := "DATA WAIT"
p.reason := "Outer geometry required by this family's target, stop or cancellation"
else
p.state := "WATCH"
p.reason := "Waiting for confirmed public-baseline prerequisite evaluation"
p
f_camRefreshPlay(Play p, CamLevels m, CamLevels prior, float atr, float opening, float barOpen, float barClose, bool observationValid, bool inWindow, bool sourceReady, string dataReason, bool smaReady) =>
[bias, ratio, width, rangeOk, widthOk, openOk, regionOk, blocked, candidate, approaching] = f_camPrerequisites(p.code, m, prior, atr, opening, barOpen, barClose, p.blocked, rangeThresholdPct, widthThresholdPct, allowNeutral)
p.rangeOk := rangeOk
p.widthOk := widthOk
p.openOk := openOk
p.regionOk := observationValid and regionOk
p.blocked := observationValid and inWindow ? blocked : p.blocked
p.candidate := sourceReady and observationValid and inWindow and candidate and p.state != "DISABLED"
p.approaching := p.candidate and p.regionOk
p.eligible := p.candidate
p.smaOk := smaReady
if p.state == "WATCH" or p.state == "DATA WAIT"
bool prerequisitesAvailable = sourceReady and not na(opening) and not na(atr) and f_camSign(atr) > 0 and (p.family == "A" or m.outerValid)
p.state := prerequisitesAvailable ? "WATCH" : "DATA WAIT"
string widthNeed = p.family == "A" or p.family == "E" ? "Wide or Similar" : p.family == "F" or p.family == "G" ? "any width" : "Narrow or Similar"
p.reason := not sourceReady ? dataReason : na(opening) ? "Exact source-minute opening unavailable" : not prerequisitesAvailable ? "Required geometry or confirmed ATR unavailable" : not inWindow ? "Outside decision window" : not observationValid ? "Malformed chart OHLC · price evaluation withheld" : p.blocked ? "Bad OPEN crossed this family's limit · blocked for this session" : not rangeOk ? "Close-delta context is " + (bias == 1 ? "Higher" : bias == -1 ? "Lower" : "Neutral") + "; this code requires " + (p.biasRequired == 1 ? "Higher" : "Lower") : not widthOk ? "Width " + width + "; requires " + widthNeed : not openOk ? "Frozen open " + f_camExact(opening) + " is outside this family's bounded inclusive opening interval" : not regionOk ? "Candidate eligible · closed price is outside its approach region" : "Approaching · prerequisites pass; original confirmed trigger still required"
p.next := p.blocked or not rangeOk or not widthOk or not openOk ? "Next source session / changed prerequisites" : not inWindow ? "Next decision window" : p.approaching ? "Original close " + p.triggerOperator + " " + f_camExact(p.triggerPrice) + (smaGate and not p.smaOk ? " plus SMA confirmation" : " with the stated setup") : "Enter the play's approach region on a confirmed close"
p
f_camRearm(Play p, int bar, bool inWindow, bool enabled, int cooldown, int episodeLimit) =>
if enabled and inWindow and f_camTerminal(p.state) and not na(p.endBar) and bar > p.endBar and bar - p.endBar >= cooldown and p.episode < episodeLimit and p.candidate and not p.blocked
p.state := "WATCH"
p.setupBar := na
p.setupTime := na
p.armedHigh := na
p.armedLow := na
p.activationBar := na
p.activationTime := na
p.activationSeq := na
p.entry := na
p.stop := na
p.target := na
p.effectiveR := na
p.endBar := na
p.endTime := na
p.outcomePrice := na
p.ambiguous := false
p.reason := "Cooldown completed · waiting for a new original setup"
p
// Explanations report the observed family trigger without changing its lifecycle.
f_camActivationReason(Play p, float activationClose) =>
bool longSide = p.dir == 1
string tier = p.family == "A" ? (longSide ? "L3" : "H3") : p.family == "E" ? (longSide ? "L4" : "H4") : p.family == "F" or p.family == "G" ? (longSide ? "L6" : "H6") : (longSide ? "H4" : "L4")
string cause = p.family == "A" ? tier + " touch + " + (longSide ? "reclaim" : "rejection") : p.family == "B" ? tier + " closed break, later retest" : p.family == "C" or p.family == "D" ? "Bounded opening region; " + tier + " retest held" : p.family == "E" ? "Closed " + (longSide ? "below " : "above ") + tier + ", later failure" : tier + " outer touch + recovery"
cause + "; close " + f_camReadable(activationClose) + (longSide ? " > " : " < ") + tier + " " + f_camReadable(p.level) + (p.family == "D" ? " · against frozen structure" : "")
// Parameterized OHLC/clock step for production and exact-function fixture probes.
// Input p is a single episode row. Only its reference fields and event arrays mutate.
f_camStep(Play p, CamLevels m, float barOpen, float barHigh, float barLow, float barClose, float previousClose, int bar, int stamp, bool contained, bool sessionEnded, bool wide, float tick, int setupLimit, int activeLimit, int bufferTicks, float minimumR) =>
bool usable = contained and f_camBarValid(barOpen, barHigh, barLow, barClose)
if p.state == "ACTIVE"
[kind, price, ambiguous, outcomeReason] = f_camOutcome(p, barOpen, barHigh, barLow, bar, stamp, usable, activeLimit, sessionEnded)
if kind != ""
f_camEnd(p, kind, outcomeReason, price, bar, stamp, ambiguous)
else if p.state == "WATCH" or p.state == "ARMED"
bool longSide = p.dir == 1
bool touched = usable and f_camTouched(p.level, barHigh, barLow)
if usable and p.state == "WATCH" and p.approaching
bool shouldArm = p.family == "B" ? not na(previousClose) and f_camCompare(previousClose, p.level) * p.dir <= 0 and f_camCompare(barClose, p.level) * p.dir > 0 : p.family == "E" ? f_camCompare(barClose, p.level) * p.dir < 0 : p.family == "C" or p.family == "D" ? true : touched
if shouldArm
string armReason = p.family == "B" ? "Confirmed breakout observed · await a later retest" : p.family == "E" ? "Confirmed excursion observed · await a later failure" : p.family == "C" or p.family == "D" ? "Known opening in bounded fourth-to-sixth interval · wait for contained retest" : "Actual bar range touched the reference level"
f_camArm(p, barHigh, barLow, bar, stamp, armReason)
p.next := p.family == "E" ? "Later close " + (longSide ? "above " : "below ") + f_camExact(p.level) : "Close " + (longSide ? "above " : "below ") + f_camExact(p.level) + (p.family == "B" ? " after a later retest" : " after the observed test")
if p.state == "ARMED"
int age = bar - p.setupBar
if age > setupLimit
f_camEnd(p, "EXPIRED", "Setup deadline passed · confirmation allowed only through age " + str.tostring(setupLimit), na, bar, stamp, false)
else if usable
p.armedHigh := math.max(p.armedHigh, barHigh)
p.armedLow := math.min(p.armedLow, barLow)
bool cancelled = p.blocked or (p.family == "A" ? (longSide ? f_camCompare(barClose, m.l4) <= 0 : f_camCompare(barClose, m.h4) >= 0) : p.family == "B" or p.family == "C" or p.family == "D" ? (longSide ? f_camCompare(barClose, m.h3) <= 0 : f_camCompare(barClose, m.l3) >= 0) : p.family == "E" ? (longSide ? f_camCompare(barClose, m.l6) <= 0 : f_camCompare(barClose, m.h6) >= 0) : false)
bool recoveryClose = f_camCompare(barClose, p.level) * p.dir > 0
bool previousWrongSide = not na(previousClose) and f_camCompare(previousClose, p.level) * p.dir <= 0
bool confirmed = p.family == "B" ? bar > p.setupBar and touched and recoveryClose : p.family == "E" ? bar > p.setupBar and recoveryClose : p.family == "C" or p.family == "D" ? touched and recoveryClose : recoveryClose and ((bar == p.setupBar and touched) or previousWrongSide)
if cancelled
f_camEnd(p, "INVALIDATED", p.blocked ? "Bad OPEN crossed the public-baseline limit · session block latched" : "Pending close crossed the original cancellation boundary", barClose, bar, stamp, false)
else if sessionEnded
f_camEnd(p, "EXPIRED", "Decision session ended before activation · no entry inferred", na, bar, stamp, false)
else if confirmed and p.approaching and p.smaOk
[planOk, stop, target, rewardR, planReason] = f_camPlanOptions(p.dir, p.family, m, p.armedHigh, p.armedLow, barClose, wide, tick, bufferTicks, minimumR, p.atrUsed, p.stopMode, stopAtrMultiple, p.targetMode, targetAtrMultiple, maxTargetAtr)
if planOk
p.state := "ACTIVE"
p.entry := barClose
p.stop := stop
p.target := target
p.effectiveR := rewardR
p.activationBar := bar
p.activationTime := stamp
p.reason := f_camActivationReason(p, barClose)
p.next := "Observe later bars: stop " + f_camExact(stop) + " / destination " + f_camExact(target)
p.invalidation := "Fixed reference stop " + f_camExact(stop) + "; session end or active deadline"
p.activationSeq := f_camEmit(p, "ACTIVE", p.reason, barClose, stamp, false)
else
f_camEnd(p, "REJECTED", planReason + " · episode consumed", barClose, bar, stamp, false)
else if sessionEnded
f_camEnd(p, "EXPIRED", "Session end observed across an unavailable boundary · no fill inferred", na, bar, stamp, false)
p
f_camClock(int barTime, int dailyStart, int dailyEnd, string mode, string sessionText, string timezone, bool weekdaysOnly) =>
int startStamp = dailyStart
int endStamp = dailyEnd
bool dayAllowed = true
string label = "Exchange day · provider boundaries"
if mode != "Exchange day"
string interval = mode == "US cash" ? "0930-1600" : sessionText
string zone = mode == "US cash" ? "America/New_York" : timezone
int startHour = int(str.tonumber(str.substring(interval, 0, 2)))
int startMinute = int(str.tonumber(str.substring(interval, 2, 4)))
int endHour = int(str.tonumber(str.substring(interval, 5, 7)))
int endMinute = int(str.tonumber(str.substring(interval, 7, 9)))
int firstMinute = startHour * 60 + startMinute
int lastMinute = endHour * 60 + endMinute
int nowMinute = hour(barTime, zone) * 60 + minute(barTime, zone)
bool overnight = lastMinute <= firstMinute
int dayOffset = overnight and nowMinute < firstMinute ? -1 : 0
int localYear = year(barTime, zone)
int localMonth = month(barTime, zone)
int localDay = dayofmonth(barTime, zone) + dayOffset
startStamp := timestamp(zone, localYear, localMonth, localDay, startHour, startMinute)
endStamp := timestamp(zone, localYear, localMonth, localDay + (overnight ? 1 : 0), endHour, endMinute)
int tradingWeekday = dayofweek(lastMinute < firstMinute ? endStamp : startStamp, zone)
dayAllowed := (mode == "Custom" and not weekdaysOnly) or (tradingWeekday >= dayofweek.monday and tradingWeekday <= dayofweek.friday)
label := (mode == "US cash" ? "US cash · " : "Custom · ") + interval + " " + zone
[startStamp, endStamp, dayAllowed, label]
f_camOpenLocation(float opening, CamLevels m) =>
na(opening) ? "OPEN UNKNOWN" : not m.valid ? "MAP UNAVAILABLE" : f_camCompare(opening, m.l4) < 0 ? "Below L4" : f_camCompare(opening, m.l4) == 0 ? "At L4" : f_camCompare(opening, m.l3) < 0 ? "L4–L3" : f_camCompare(opening, m.l3) == 0 ? "At L3" : f_camCompare(opening, m.h3) < 0 ? "Inside third band" : f_camCompare(opening, m.h3) == 0 ? "At H3" : f_camCompare(opening, m.h4) < 0 ? "H3–H4" : f_camCompare(opening, m.h4) == 0 ? "At H4" : "Above H4"
//
// SOURCE ADAPTER · one-minute observations, session-frozen scalar packet.
// Lower-timeframe request returns an already frozen context, never one candle's
// OHLC as a proxy for an entire source window. Availability is checked again below.
f_camBookFinish(CamWindowBook book, bool complete, string reason) =>
book.prior := book.latest
book.latest := f_camLevels(book.h, book.l, book.c, book.start, book.finish, complete, reason)
book.finished := true
book.complete := complete
book.reason := reason
book
f_camObserve(CamWindowBook book, int start, int finish, bool allowed, int stamp, int closingStamp, float o, float h, float l, float c) =>
bool overlap = allowed and not na(start) and not na(finish) and stamp < finish and closingStamp > start
bool contained = overlap and stamp >= start and closingStamp <= finish
bool valid = f_camBarValid(o, h, l, c)
if not book.finished and (stamp >= book.finish or overlap and start != book.start)
f_camBookFinish(book, false, "Exact final source boundary missing")
if overlap
if na(book.start) or book.start != start
book.start := start
book.finish := finish
book.lastClose := na
book.h := na
book.l := na
book.c := na
book.samples := 0
book.complete := contained and valid and stamp == start
book.finished := false
book.reason := book.complete ? "Collecting contiguous 1m source" : "Exact valid source opening missing"
if not book.finished
if not contained or not valid or not na(book.lastClose) and stamp != book.lastClose
book.complete := false
book.reason := not contained ? "Source minute straddles boundary" : not valid ? "Malformed source OHLC" : "Missing interior source minute"
if contained
if valid
book.h := na(book.h) ? h : math.max(book.h, h)
book.l := na(book.l) ? l : math.min(book.l, l)
book.c := c
book.samples += 1
book.lastClose := closingStamp
if closingStamp >= finish
bool complete = book.complete and contained and closingStamp == finish
f_camBookFinish(book, complete, complete ? "Complete observed 1m interval" : book.reason)
book
// Original observed-session ATR convention, not an undisclosed vendor formula.
// Missing calendar days are not observations; invalid observed windows reset.
// Empty/duplicate/out-of-order packets cannot count twice or rewind the chain.
f_camAtrConsume(CamAtrBook book, CamLevels source) =>
if not na(source.sourceEnd) and (na(book.observedEnd) or source.sourceEnd > book.observedEnd)
book.observedEnd := source.sourceEnd
bool valid = source.complete and not na(source.sourceStart) and source.sourceEnd > source.sourceStart and (na(book.sourceEnd) or source.sourceStart >= book.sourceEnd) and f_camBarValid(source.c, source.h, source.l, source.c)
if valid
float tr = na(book.previousClose) ? source.h - source.l : math.max(source.h - source.l, math.max(math.abs(source.h - book.previousClose), math.abs(source.l - book.previousClose)))
if book.samples == 0
book.sourceStart := source.sourceStart
book.samples += 1
if book.samples <= 14
book.seedSum += tr
book.value := book.samples == 14 ? book.seedSum / 14 : na
else
book.value := (book.value * 13 + tr) / 14
book.previousClose := source.c
book.sourceEnd := source.sourceEnd
else
book.samples := 0
book.seedSum := 0
book.previousClose := na
book.value := na
book.sourceStart := na
book.sourceEnd := na
book
f_camAtrBasisCode(string mode, bool futures, string resolvedDecision) =>
mode == "RTH session" or mode == "Market default" and futures and resolvedDecision != "Exchange day" ? 2 : 1
f_camAtrBasisName(int code) =>
code == 2 ? "Observed RTH sessions · Wilder14" : code == 1 ? "Provider daily · ATR14" : "Unavailable"
f_camAtrReady(float value, int sourceEnd, int cutoff) =>
not na(value) and f_camSign(value) > 0 and not na(sourceEnd) and not na(cutoff) and sourceEnd <= cutoff and cutoff - sourceEnd <= 604800000
f_camAtrFreeze(CamSourcePacket packet, CamAtrBook book, int basis, float providerAtr, int providerStamp, int cutoff) =>
packet.atrBasisCode := basis
packet.atr := basis == 2 ? book.value : providerAtr
packet.atrStamp := basis == 2 ? book.sourceEnd : providerStamp
packet.atrSamples := basis == 2 ? book.samples : not na(providerAtr) ? 14 : 0
packet.atrStart := basis == 2 ? book.sourceStart : na
packet.atrReady := f_camAtrReady(packet.atr, packet.atrStamp, cutoff)
packet
f_camSourceCode(string kind) =>
kind == "RTH" ? 1 : kind == "ETH" ? 2 : kind == "Exchange daily" ? 3 : kind == "Selected session" ? 4 : 0
f_camSourceName(int code) =>
code == 1 ? "RTH" : code == 2 ? "ETH" : code == 3 ? "Exchange daily" : code == 4 ? "Selected session" : "Unavailable"
f_camSourceReason(int code) =>
code == 1 ? "Complete source pair frozen before the opening minute" : code == 2 ? "Auto requires complete prior postmarket/current premarket ending at RTH open; choose a fixed source if unavailable" : code == 3 ? "Two complete chosen H/L and close windows required" : code == 4 ? "Source or independent-close timestamps not ordered" : code == 5 ? "Source or ATR unavailable before decision open" : code == 6 ? "Source or close older than seven elapsed days" : code == 7 ? "Chosen current inner geometry unavailable" : code == 8 ? "Chosen previous inner geometry unavailable" : code == 9 ? "Selected ATR unavailable, warming or stale" : code == 10 ? "Exact valid one-minute opening unavailable" : "Waiting for an observed source opening"
f_camSelectionReason(int code) =>
code == 6 ? "Market default: Exchange daily for crypto; 24/7 provider clock" : code == 7 ? "Market default: RTH for non-crypto; configured cash-session source" : code == 1 ? "Auto crypto: Exchange daily, 24/7 provider clock" : code == 2 ? "Fixed source; no automatic session substitution" : code == 3 ? "ETH selected: pre-opening excursion exceeded selected ATR tolerance" : code == 4 ? "RTH selected: complete pre-opening excursion stayed within tolerance" : "Auto selection lacks complete aligned pre/post windows"
f_camSourceEngine() =>
// Explicit ticker/timeframe in this nested request; every field is completed.
[dh1, dl1, dc1, ds1, de1, dh2, dl2, dc2, ds2, de2, dailyAtr] = request.security(syminfo.tickerid, "D", [high[1], low[1], close[1], time[1], time_close[1], high[2], low[2], close[2], time[2], time_close[2], ta.atr(14)[1]], gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_on, calc_bars_count = 500)
int exchangeStart = time("D")
int exchangeEnd = time_close("D")
bool crypto = syminfo.type == "crypto"
string resolvedDecision = decisionMode == "Auto" ? (crypto ? "Exchange day" : "US cash") : decisionMode
[decisionStart, decisionEnd, decisionAllowed, clockLabel] = f_camClock(time, exchangeStart, exchangeEnd, resolvedDecision, customSession, customTimezone, customWeekdays)
[rthStart, rthEnd, rthAllowed, rthLabel] = f_camClock(time, exchangeStart, exchangeEnd, "Custom", regularSession, regularTimezone, not crypto)
string ethMode = extendedClock == "Auto" and crypto ? "Exchange day" : "Custom"
string ethText = extendedClock == "Custom" ? extendedSession : syminfo.type == "futures" ? "1800-1700" : "0400-2000"
string ethZone = extendedClock == "Custom" ? extendedTimezone : "America/New_York"
[ethStart, ethEnd, ethAllowed, ethLabel] = f_camClock(time, exchangeStart, exchangeEnd, ethMode, ethText, ethZone, not crypto)
// Find the RTH opening belonging to this ETH interval using local calendar dates.
[regularAtEthStart, regularEndAtEthStart, ignoredDay, ignoredLabel] = f_camClock(ethStart, exchangeStart, exchangeEnd, "Custom", regularSession, regularTimezone, not crypto)
int nextRegular = regularAtEthStart
if not na(nextRegular) and nextRegular < ethStart
nextRegular := timestamp(regularTimezone, year(nextRegular, regularTimezone), month(nextRegular, regularTimezone), dayofmonth(nextRegular, regularTimezone) + 1, hour(nextRegular, regularTimezone), minute(nextRegular, regularTimezone))
[pairedRegularStart, pairedRegularEnd, pairedDay, pairedLabel] = f_camClock(nextRegular, exchangeStart, exchangeEnd, "Custom", regularSession, regularTimezone, not crypto)
var CamAtrBook rthAtr = CamAtrBook.new()
var CamWindowBook rth = CamWindowBook.new(CamLevels.new(), CamLevels.new())
var CamWindowBook eth = CamWindowBook.new(CamLevels.new(), CamLevels.new())
var CamWindowBook selected = CamWindowBook.new(CamLevels.new(), CamLevels.new())
var CamWindowBook pre = CamWindowBook.new(CamLevels.new(), CamLevels.new())
var CamWindowBook post = CamWindowBook.new(CamLevels.new(), CamLevels.new())
var CamSourcePacket frozen = CamSourcePacket.new(current = CamLevels.new(), prior = CamLevels.new())
bool overlap = decisionAllowed and time < decisionEnd and time_close > decisionStart
bool newDecision = overlap and (na(frozen.sessionStart) or frozen.sessionStart != decisionStart)
// Certify any missed closing boundary before selecting predecessors.
if not rth.finished and time >= rth.finish
f_camBookFinish(rth, false, "Exact RTH end was not observed")
if not eth.finished and time >= eth.finish
f_camBookFinish(eth, false, "Exact ETH end was not observed")
if not selected.finished and time >= selected.finish
f_camBookFinish(selected, false, "Exact selected-window end was not observed")
// A missed observed closing boundary is consumed before freezing this opening.
f_camAtrConsume(rthAtr, rth.latest)
if newDecision
CamSourcePacket next = CamSourcePacket.new(current = CamLevels.new(), prior = CamLevels.new())
next.sessionStart := decisionStart
next.sessionEnd := decisionEnd
next.cutoff := decisionStart
next.clockLabel := clockLabel
next.openKnown := time == decisionStart and time_close <= decisionEnd and f_camBarValid(open, high, low, close)
next.opening := next.openKnown ? open : na
next.openingTime := next.openKnown ? time : na
int atrBasis = f_camAtrBasisCode(atrBasisMode, syminfo.type == "futures", resolvedDecision)
f_camAtrFreeze(next, rthAtr, atrBasis, dailyAtr, de1, decisionStart)
CamLevels daily1 = f_camLevels(dh1, dl1, dc1, ds1, de1, not na(de1), "Completed provider daily source unavailable")
CamLevels daily2 = f_camLevels(dh2, dl2, dc2, ds2, de2, not na(de2), "Second provider daily source unavailable")
string chosen = sourceMode == "Market default" ? (crypto ? "Exchange daily" : "RTH") : sourceMode == "Auto RTH/ETH" and crypto ? "Exchange daily" : sourceMode
bool selectionReady = true
if chosen == "Auto RTH/ETH"
bool postReady = post.latest.complete and post.latest.sourceStart == rth.latest.sourceEnd and post.latest.sourceEnd <= decisionStart
bool preReady = pre.latest.complete and pre.latest.sourceEnd == decisionStart and pre.latest.sourceStart >= rth.latest.sourceEnd
bool alignedOpening = decisionStart == nextRegular
selectionReady := rth.latest.complete and eth.latest.complete and postReady and preReady and alignedOpening and next.atrReady
next.excursion := selectionReady ? math.max(math.max(post.latest.h, pre.latest.h) - rth.latest.h, rth.latest.l - math.min(post.latest.l, pre.latest.l)) : na
next.excursionThreshold := next.atr * switchingTolerance / 100
bool outside = selectionReady and (f_camCompare(math.max(post.latest.h, pre.latest.h), rth.latest.h + next.excursionThreshold) > 0 or f_camCompare(math.min(post.latest.l, pre.latest.l), rth.latest.l - next.excursionThreshold) < 0)
chosen := outside ? "ETH" : "RTH"
next.selectionReason := not selectionReady ? "Auto requires complete prior postmarket and current premarket ending at the chosen RTH open; choose a fixed source if unavailable" : outside ? "ETH selected: observed excursion exceeded selected ATR tolerance before open" : "RTH selected: complete observed excursion stayed within tolerance at open"
next.selectionCode := not selectionReady ? 5 : outside ? 3 : 4
else
next.selectionCode := sourceMode == "Market default" ? (crypto ? 6 : 7) : sourceMode == "Auto RTH/ETH" and crypto ? 1 : 2
next.selectionReason := f_camSelectionReason(next.selectionCode)
CamLevels main1 = chosen == "RTH" ? rth.latest : chosen == "ETH" ? eth.latest : chosen == "Selected session" ? selected.latest : daily1
CamLevels main2 = chosen == "RTH" ? rth.prior : chosen == "ETH" ? eth.prior : chosen == "Selected session" ? selected.prior : daily2
string chosenClose = closeMode == "Match source" ? chosen : closeMode
CamLevels close1 = chosenClose == "RTH" ? rth.latest : chosenClose == "ETH" ? eth.latest : chosenClose == "Selected session" ? selected.latest : daily1
CamLevels close2 = chosenClose == "RTH" ? rth.prior : chosenClose == "ETH" ? eth.prior : chosenClose == "Selected session" ? selected.prior : daily2
next.sourceKind := chosen + (chosen == "Exchange daily" ? " · provider H/L" : " · observed 1m H/L")
next.closeKind := chosenClose + (chosenClose == "Exchange daily" ? " · provider daily/settlement C" : " · observed last-trade C")
next.sourceCode := f_camSourceCode(chosen)
next.closeCode := f_camSourceCode(chosenClose)
next.closeSourceEnd := close1.sourceEnd
next.priorCloseSourceEnd := close2.sourceEnd
bool completePair = main1.complete and main2.complete and close1.complete and close2.complete
bool ordered = not na(main1.sourceStart) and not na(main1.sourceEnd) and not na(main2.sourceEnd) and main2.sourceEnd <= main1.sourceStart and close2.sourceEnd <= close1.sourceStart
bool available = main1.sourceEnd <= decisionStart and close1.sourceEnd <= decisionStart and next.atrStamp <= decisionStart
bool fresh = not na(main1.sourceEnd) and decisionStart - main1.sourceEnd <= 604800000 and not na(close1.sourceEnd) and decisionStart - close1.sourceEnd <= 604800000
next.current := f_camLevels(main1.h, main1.l, close1.c, main1.sourceStart, main1.sourceEnd, completePair and selectionReady, "Required H/L or selected close predecessor is incomplete", false)
next.prior := f_camLevels(main2.h, main2.l, close2.c, main2.sourceStart, main2.sourceEnd, completePair and selectionReady, "Previous chosen H/L or close source is incomplete", false)
next.ready := selectionReady and completePair and ordered and available and fresh and next.current.valid and next.prior.valid and next.atrReady
next.reason := not selectionReady ? next.selectionReason : not completePair ? "Two complete chosen H/L and close windows required" : not ordered ? "Source or independent-close timestamps not ordered" : not available ? "Source or ATR was unavailable before decision open" : not fresh ? "Source or close is older than seven elapsed days" : not next.current.valid ? next.current.reason : not next.prior.valid ? next.prior.reason : not next.atrReady ? "Selected ATR unavailable, warming or stale" : not next.openKnown ? "Exact valid one-minute opening unavailable" : "Complete source pair frozen before the opening minute"
next.reasonCode := not selectionReady ? 2 : not completePair ? 3 : not ordered ? 4 : not available ? 5 : not fresh ? 6 : not next.current.valid ? 7 : not next.prior.valid ? 8 : not next.atrReady ? 9 : not next.openKnown ? 10 : 1
next.rthSamples := rth.samples
next.ethSamples := eth.samples
next.selectedSamples := selected.samples
next.rthComplete := rth.latest.complete
next.ethComplete := eth.latest.complete
next.selectedComplete := selected.latest.complete
next.coverage := "1m source · RTH " + rth.reason + " / ETH " + eth.reason + "; up to 100,000 source minutes"
frozen := next
// Current minute cannot affect this decision's selected map or auto choice.
f_camObserve(rth, rthStart, rthEnd, rthAllowed, time, time_close, open, high, low, close)
f_camAtrConsume(rthAtr, rth.latest)
f_camObserve(eth, ethStart, ethEnd, ethAllowed, time, time_close, open, high, low, close)
f_camObserve(selected, decisionStart, decisionEnd, decisionAllowed, time, time_close, open, high, low, close)
f_camObserve(pre, ethStart, nextRegular, ethAllowed and pairedDay and ethStart < nextRegular, time, time_close, open, high, low, close)
f_camObserve(post, pairedRegularEnd, ethEnd, ethAllowed and pairedDay and pairedRegularEnd < ethEnd, time, time_close, open, high, low, close)
// Scalar-only transport is essential: returning nested UDTs across 100k
// source minutes exhausted actual TradingView runtime memory in development.
[frozen.current.h, frozen.current.l, frozen.current.c, frozen.current.sourceStart, frozen.current.sourceEnd, frozen.current.complete, frozen.prior.h, frozen.prior.l, frozen.prior.c, frozen.prior.sourceStart, frozen.prior.sourceEnd, frozen.prior.complete, frozen.ready, frozen.openKnown, frozen.opening, frozen.openingTime, frozen.sessionStart, frozen.sessionEnd, frozen.cutoff, frozen.atr, frozen.atrStamp, frozen.closeSourceEnd, frozen.priorCloseSourceEnd, frozen.excursion, frozen.excursionThreshold, frozen.sourceCode, frozen.closeCode, frozen.reasonCode, frozen.selectionCode, frozen.rthSamples, frozen.ethSamples, frozen.selectedSamples, frozen.rthComplete, frozen.ethComplete, frozen.selectedComplete, frozen.atrBasisCode, frozen.atrSamples, frozen.atrStart]
// Modify only session selection; retain the chart ticker's other price modifiers.
string camSourceTicker = ticker.modify(syminfo.tickerid, session = session.extended)
[camRawH1, camRawL1, camRawC1, camRawStart1, camRawEnd1, camRawComplete1, camRawH2, camRawL2, camRawC2, camRawStart2, camRawEnd2, camRawComplete2, camRawReady, camRawOpenKnown, camRawOpen, camRawOpenTime, camRawSessionStart, camRawSessionEnd, camRawCutoff, camRawAtr, camRawAtrStamp, camRawCloseEnd1, camRawCloseEnd2, camRawExcursion, camRawExcursionThreshold, camRawSourceCode, camRawCloseCode, camRawReasonCode, camRawSelectionCode, camRawRthSamples, camRawEthSamples, camRawSelectedSamples, camRawRthComplete, camRawEthComplete, camRawSelectedComplete, camRawAtrBasisCode, camRawAtrSamples, camRawAtrStart] = request.security(camSourceTicker, "1", f_camSourceEngine(), gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_off, calc_bars_count = 100000)
var CamSourcePacket camSourcePacket = CamSourcePacket.new(current = CamLevels.new(), prior = CamLevels.new())
int camExchangeStart = time("D")
int camExchangeEnd = time_close("D")
string camResolvedDecision = decisionMode == "Auto" ? (syminfo.type == "crypto" ? "Exchange day" : "US cash") : decisionMode
[camScheduledStart, camScheduledEnd, camDayAllowed, camClockLabel] = f_camClock(time, camExchangeStart, camExchangeEnd, camResolvedDecision, customSession, customTimezone, customWeekdays)
bool camChartSupported = chart.is_standard and timeframe.isminutes and timeframe.multiplier >= 1 and timeframe.multiplier <= 60
bool camOverlapNow = camChartSupported and camDayAllowed and not na(time_close) and time < camScheduledEnd and time_close > camScheduledStart
bool camContainedNow = camOverlapNow and time >= camScheduledStart and time_close <= camScheduledEnd
bool camBarOhlcValid = f_camBarValid(open, high, low, close)
float camSma = ta.sma(close, smaLength)
var CamLevels camMap = CamLevels.new()
var CamLevels camPriorMap = CamLevels.new()
var int camSessionStart = na
var int camSessionEnd = na
var int camOpenTime = na
var float camOpen = na
var bool camOpenKnown = false
var bool camMapReady = false
var bool camSourceStale = false
var string camDataReason = "Waiting for first fully contained decision candle"
var string camRelationship = "Unavailable"
var string camWidthLabel = "Unavailable"
var string camOpenLocation = "OPEN UNKNOWN"
var string camDecisionLabel = "Decision window not observed"
var string camSourceLabel = "Source not frozen"
var string camCloseLabel = "Close source not frozen"
var string camSourceSelectionReason = "Waiting for source cutoff"
var int camSourceCutoff = na
var int camOpeningSourceTime = na
var int camAtrStamp = na
var float camAtrReference = na
var string camAtrBasis = "Unavailable"
var bool camAtrReady = false
var int camAtrSamples = 0
var int camAtrStart = na
var float camRangeDelta = na
var float camRangeThreshold = na
var int camBias = 0
var float camWidthRatio = na
var int camClosedBar = na
var int camClosedTime = na
var float camClosedPrice = na
var bool camInSession = false
var float camPreviousClose = na
var string camSelectedCoverage = "Waiting for 1m source snapshot"
var int camSelectedCount = 0
var int camActiveCount = 0
var int camHigherActive = 0
var int camLowerActive = 0
var int camCandidateCount = 0
var int camApproachingCount = 0
var bool camSessionEndedOnce = false
var int camPreviousObservedTime = na
var int camPreviousObservedClose = na
bool camSessionStartPulse = false
bool camSessionEndPulse = false
bool camActivated = false
bool camEnded = false
bool camArmed = false
int camActivationCount = 0
int camEndingCount = 0
if array.size(plays) == 0
for i = 0 to 13
array.push(plays, f_camNewPlay(i, camMap, false, "Waiting for supported chart and source opening", 0, na, false, na))
if barstate.isconfirmed
camClosedBar := bar_index
camClosedTime := time_close
camClosedPrice := camBarOhlcValid ? close : na
array.clear(camBarEvents)
if not camChartSupported
camDataReason := "Unsupported chart · use standard minute candles, 1m through 60m"
camDecisionLabel := "UNSUPPORTED CHART"
camSelectedCoverage := "DISABLED · unsupported chart"
bool newDecision = camContainedNow and (na(camSessionStart) or camScheduledStart != camSessionStart)
if newDecision
for i = 0 to 13
Play old = array.get(plays, i)
if old.state == "ACTIVE" or old.state == "ARMED"
f_camEnd(old, "EXPIRED", "Late-observed session rollover · no unobserved closing fill inferred", na, bar_index, time_close, false)
camSessionStartPulse := true
camSessionStart := camScheduledStart
camSessionEnd := camScheduledEnd
camSessionEndedOnce := false
// Reconstruct cross-context data once per decision, never once per minute.
CamSourcePacket admitted = CamSourcePacket.new(current = f_camLevels(camRawH1, camRawL1, camRawC1, camRawStart1, camRawEnd1, camRawComplete1, "Incomplete current source", false), prior = f_camLevels(camRawH2, camRawL2, camRawC2, camRawStart2, camRawEnd2, camRawComplete2, "Incomplete previous source", false))
admitted.ready := camRawReady
admitted.openKnown := camRawOpenKnown
admitted.opening := camRawOpen
admitted.openingTime := camRawOpenTime
admitted.sessionStart := camRawSessionStart
admitted.sessionEnd := camRawSessionEnd
admitted.cutoff := camRawCutoff
admitted.atr := camRawAtr
admitted.atrStamp := camRawAtrStamp
admitted.atrBasisCode := camRawAtrBasisCode
admitted.atrSamples := camRawAtrSamples
admitted.atrStart := camRawAtrStart
admitted.atrReady := f_camAtrReady(camRawAtr, camRawAtrStamp, camRawCutoff)
admitted.closeSourceEnd := camRawCloseEnd1
admitted.priorCloseSourceEnd := camRawCloseEnd2
admitted.excursion := camRawExcursion
admitted.excursionThreshold := camRawExcursionThreshold
admitted.sourceCode := camRawSourceCode
admitted.closeCode := camRawCloseCode
admitted.reasonCode := camRawReasonCode
admitted.selectionCode := camRawSelectionCode
admitted.sourceKind := f_camSourceName(camRawSourceCode) + (camRawSourceCode == 3 ? " · provider H/L" : " · observed 1m H/L")
admitted.closeKind := f_camSourceName(camRawCloseCode) + (camRawCloseCode == 3 ? " · provider daily/settlement C" : " · observed last-trade C")
admitted.reason := f_camSourceReason(camRawReasonCode)
admitted.selectionReason := f_camSelectionReason(camRawSelectionCode)
admitted.rthSamples := camRawRthSamples
admitted.ethSamples := camRawEthSamples
admitted.selectedSamples := camRawSelectedSamples
admitted.rthComplete := camRawRthComplete
admitted.ethComplete := camRawEthComplete
admitted.selectedComplete := camRawSelectedComplete
admitted.coverage := "1m source · RTH " + (camRawRthComplete ? "complete " : "incomplete ") + str.tostring(camRawRthSamples) + " bars; ETH " + (camRawEthComplete ? "complete " : "incomplete ") + str.tostring(camRawEthSamples) + "; up to 100,000 source minutes"
camSourcePacket := admitted
bool packetMatches = not na(camSourcePacket) and camSourcePacket.sessionStart == camSessionStart and camSourcePacket.cutoff == camSessionStart and camSourcePacket.cutoff <= time
bool chartOpeningObserved = time == camSessionStart or not na(camPreviousObservedTime) and camPreviousObservedTime <= camSessionStart and camPreviousObservedClose >= camSessionStart
camMap := packetMatches ? camSourcePacket.current : CamLevels.new()
camPriorMap := packetMatches ? camSourcePacket.prior : CamLevels.new()
camOpenKnown := packetMatches and camSourcePacket.openKnown and chartOpeningObserved
camOpen := camOpenKnown ? camSourcePacket.opening : na
camOpenTime := camOpenKnown ? camSourcePacket.openingTime : na
camOpeningSourceTime := camOpenTime
camMapReady := packetMatches and camSourcePacket.ready
camSourceCutoff := packetMatches ? camSourcePacket.cutoff : na
camAtrReference := packetMatches ? camSourcePacket.atr : na
camAtrStamp := packetMatches ? camSourcePacket.atrStamp : na
camAtrBasis := packetMatches ? f_camAtrBasisName(camSourcePacket.atrBasisCode) : "Unavailable"
camAtrReady := packetMatches and camSourcePacket.atrReady
camAtrSamples := packetMatches ? camSourcePacket.atrSamples : 0
camAtrStart := packetMatches ? camSourcePacket.atrStart : na
camSourceLabel := packetMatches ? camSourcePacket.sourceKind : "Source packet unavailable at this chart opening"
camCloseLabel := packetMatches ? camSourcePacket.closeKind : "Unavailable"
camSourceSelectionReason := packetMatches ? camSourcePacket.selectionReason : "No matching session snapshot admitted"
camSelectedCoverage := packetMatches ? camSourcePacket.coverage : "Source history unavailable or packet identity differs"
camDecisionLabel := camClockLabel
camDataReason := not packetMatches ? "Matching frozen source unavailable at first contained chart candle" : not camSourcePacket.ready ? camSourcePacket.reason : not chartOpeningObserved ? "Chart history starts after the opening boundary · earlier bad-price blocks are unknown" : not camOpenKnown ? "Exact valid source opening unavailable" : camSourcePacket.reason
camSourceStale := not na(camMap.sourceEnd) and camSessionStart - camMap.sourceEnd > 604800000
[relation, geometricBias, geometricRatio, geometricWidth] = f_camRelationship(camMap, camPriorMap)
camRelationship := camMapReady ? relation : "Unavailable"
[newBias, ratio, widthLabel, rangeOk, widthOk, openOk, regionOk, blocked, candidate, approaching] = f_camPrerequisites("HA", camMap, camPriorMap, camAtrReference, camOpen, camOpen, camOpen, false, rangeThresholdPct, widthThresholdPct, allowNeutral)
camBias := camMapReady ? newBias : 0
camWidthRatio := camMapReady ? ratio : na
camWidthLabel := camMapReady ? widthLabel : "Unavailable"
camRangeDelta := camMapReady ? camMap.c - camPriorMap.c : na
camRangeThreshold := camAtrReference * rangeThresholdPct / 100
camOpenLocation := f_camOpenLocation(camOpen, camMap)
camPreviousClose := camOpen
for i = 0 to 13
Play p = f_camNewPlay(i, camMap, camMapReady, camDataReason, camBias, camOpen, camOpenKnown, camSessionStart)
p.sourceKind := camSourceLabel
p.closeKind := camCloseLabel
p.atrUsed := camAtrReference
p.atrStamp := camAtrStamp
p.atrBasis := camAtrBasis
p.atrSamples := camAtrSamples
p.atrStart := camAtrStart
p.sourceCutoff := camSourceCutoff
p.closeSourceEnd := packetMatches ? camSourcePacket.closeSourceEnd : na
p.stopMode := stopModel
p.targetMode := targetModel
// The source opening is the first observed OPEN, including on 60m charts.
if camOpenKnown
f_camRefreshPlay(p, camMap, camPriorMap, camAtrReference, camOpen, camOpen, camOpen, true, true, camMapReady, camDataReason, true)
array.set(plays, i, p)
camInSession := camContainedNow and not na(camSessionStart) and camScheduledStart == camSessionStart
bool sessionEnded = not na(camSessionEnd) and time_close >= camSessionEnd
if sessionEnded and not camSessionEndedOnce
camSessionEndPulse := true
camSessionEndedOnce := true
for i = 0 to 13
Play p = array.get(plays, i)
bool smaReady = not smaGate or not na(camSma) and bar_index >= smaLength - 1 and f_camCompare(close, camSma) * p.dir > 0
f_camRefreshPlay(p, camMap, camPriorMap, camAtrReference, camOpen, open, close, camBarOhlcValid, camInSession, camMapReady and camOpenKnown, camDataReason, smaReady)
f_camRearm(p, bar_index, camInSession and not sessionEnded, allowRearm, cooldownBars, maxEpisodes)
f_camStep(p, camMap, open, high, low, close, camPreviousClose, bar_index, time_close, camInSession, sessionEnded, camWidthLabel == "Wide", syminfo.mintick, setupDeadline, activeDeadline, stopBufferTicks, minRewardR)
if camInSession
camPreviousClose := camBarOhlcValid ? close : na
camActiveCount := 0
camHigherActive := 0
camLowerActive := 0
camCandidateCount := 0
camApproachingCount := 0
for i = 0 to 13
Play p = array.get(plays, i)
camActiveCount += p.state == "ACTIVE" ? 1 : 0
camHigherActive += p.state == "ACTIVE" and p.biasRequired == 1 ? 1 : 0
camLowerActive += p.state == "ACTIVE" and p.biasRequired == -1 ? 1 : 0
camCandidateCount += p.candidate ? 1 : 0
camApproachingCount += p.approaching ? 1 : 0
if array.size(camBarEvents) > 0
for i = 0 to array.size(camBarEvents) - 1
CamEvent e = array.get(camBarEvents, i)
camActivationCount += e.kind == "ACTIVE" ? 1 : 0
camEndingCount += f_camTerminal(e.kind) ? 1 : 0
camArmed := camArmed or e.kind == "ARMED"
camActivated := camActivationCount > 0
camEnded := camEndingCount > 0
camPreviousObservedTime := time
camPreviousObservedClose := time_close
f_camJsonString(string value) =>
string escaped = str.replace_all(value, "\\", "\\\\")
escaped := str.replace_all(escaped, "\"", "\\\"")
escaped := str.replace_all(escaped, "\n", "\\n")
escaped := str.replace_all(escaped, "\t", "\\t")
"\"" + escaped + "\""
f_camJsonNumber(float value) =>
f_camNumber(value)
f_camJsonTime(int value) =>
na(value) ? "null" : str.tostring(value)
f_camJsonBool(bool value) =>
value ? "true" : "false"
f_camSettingsId() =>
CAM_FORMULA + "|lite2-original2.1|" + atrBasisMode + "|" + sourceMode + "|" + closeMode + "|" + decisionMode + "|" + customSession + "|" + customTimezone + "|" + f_camJsonBool(customWeekdays) + "|" + regularSession + "|" + regularTimezone + "|" + extendedClock + "|" + extendedSession + "|" + extendedTimezone + "|" + f_camJsonNumber(switchingTolerance) + "|" + f_camJsonNumber(rangeThresholdPct) + "|" + f_camJsonNumber(widthThresholdPct) + "|" + f_camJsonBool(allowNeutral) + "|" + f_camJsonBool(smaGate) + "|" + str.tostring(smaLength) + "|" + f_camJsonBool(enableA) + f_camJsonBool(enableB) + f_camJsonBool(enableC) + f_camJsonBool(enableD) + f_camJsonBool(enableE) + f_camJsonBool(enableF) + f_camJsonBool(enableG) + "|" + str.tostring(setupDeadline) + "|" + str.tostring(activeDeadline) + "|" + str.tostring(stopBufferTicks) + "|" + f_camJsonNumber(minRewardR) + "|" + stopModel + "|" + f_camJsonNumber(stopAtrMultiple) + "|" + targetModel + "|" + f_camJsonNumber(targetAtrMultiple) + "|" + f_camJsonNumber(maxTargetAtr) + "|" + f_camJsonBool(allowRearm) + "|" + str.tostring(cooldownBars) + "|" + str.tostring(maxEpisodes)
f_camEventJson(CamEvent e) =>
string id = syminfo.tickerid + "|" + timeframe.period + "|" + f_camSettingsId() + "|" + str.tostring(e.sessionStart) + "|" + e.code + "|" + str.tostring(e.episode) + "|" + e.kind + "|" + str.tostring(e.eventTime)
"{\"id\":" + f_camJsonString(id) + ",\"code\":" + f_camJsonString(e.code) + ",\"episode\":" + str.tostring(e.episode) + ",\"type\":" + f_camJsonString(e.kind) + ",\"direction\":" + f_camJsonString(e.dir == 1 ? "long" : "short") + ",\"time\":" + f_camJsonTime(e.eventTime) + ",\"session_start\":" + f_camJsonTime(e.sessionStart) + ",\"source_start\":" + f_camJsonTime(e.sourceStart) + ",\"source_close\":" + f_camJsonTime(e.sourceEnd) + ",\"source_kind\":" + f_camJsonString(e.sourceKind) + ",\"close_kind\":" + f_camJsonString(e.closeKind) + ",\"selected_close_time\":" + f_camJsonTime(e.closeSourceEnd) + ",\"source_cutoff\":" + f_camJsonTime(e.sourceCutoff) + ",\"atr_time\":" + f_camJsonTime(e.atrStamp) + ",\"atr_basis\":" + f_camJsonString(e.atrBasis) + ",\"atr_value\":" + f_camJsonNumber(e.atrUsed) + ",\"atr_samples\":" + str.tostring(e.atrSamples) + ",\"atr_start\":" + f_camJsonTime(e.atrStart) + ",\"price\":" + f_camJsonNumber(e.price) + ",\"entry\":" + f_camJsonNumber(e.entry) + ",\"stop\":" + f_camJsonNumber(e.stop) + ",\"target\":" + f_camJsonNumber(e.target) + ",\"ambiguous\":" + f_camJsonBool(e.ambiguous) + ",\"reason\":" + f_camJsonString(e.reason) + "}"
if barstate.isconfirmed and array.size(camBarEvents) > 0
string selectedEvents = ""
int selectedCount = 0
for i = 0 to array.size(camBarEvents) - 1
CamEvent e = array.get(camBarEvents, i)
bool include = e.kind == "ACTIVE" ? alertActivation : e.kind == "ARMED" ? alertArmed : alertEnding and f_camTerminal(e.kind)
if include
selectedEvents += (selectedCount > 0 ? "," : "") + f_camEventJson(e)
selectedCount += 1
if selectedCount > 0
string envelope = "{\"schema\":\"camarilla-desk.v2\",\"product_version\":" + f_camJsonString(CAM_VERSION) + ",\"rule_version\":\"lite2-prerequisites.original2.1-execution\",\"formula\":" + f_camJsonString(CAM_FORMULA) + ",\"settings_id\":" + f_camJsonString(f_camSettingsId()) + ",\"source_mode\":" + f_camJsonString(sourceMode) + ",\"decision_mode\":" + f_camJsonString(decisionMode) + ",\"symbol\":" + f_camJsonString(syminfo.tickerid) + ",\"timeframe\":" + f_camJsonString(timeframe.period) + ",\"confirmed_time\":" + f_camJsonTime(time_close) + ",\"fill_confirmed\":false,\"events\":[" + selectedEvents + "]}"
alert(envelope, alert.freq_once_per_bar_close)
alertcondition(camActivated, "Camarilla play activated", "One or more original Camarilla hypotheses activated on the confirmed close. Reference plans are not order fills. Use Any alert() function call for the aggregate JSON envelope.")
alertcondition(camEnded, "Camarilla episode ended", "One or more Camarilla episodes ended on the confirmed update. Use Any alert() function call for complete event details.")
// VIEW CONTRACT v2: tracked immutable plans are independent from manual inspection.
// Chart-first Ribbon and complete seven-level map. Clean adds plan detail.
// Display inputs do not mutate candidates, episodes, alerts or reference prices.
string vBoardMode = input.string("Ribbon", "Trade HUD", options = ["Ribbon", "Clean", "Context", "Audit", "Off"], group = "Display", tooltip = "Ribbon: compact source, context and current action. Clean: expanded tracked plan. Context: up to five attention rows. Audit: all fourteen plays. Attention priority is not a strength score.", display = display.none)
string vDetail = input.string("Off", "Inspector", options = ["Off", "Brief", "Standard"], group = "Display", tooltip = "Inspect any play without replacing the independently tracked chart plan. Standard adds prerequisites and source evidence.", display = display.none)
string vTheme = input.string("Trading Dark", "Theme", options = ["Trading Dark", "Accessible Dark", "Accessible Light", "Aurora", "Royal Gold"], group = "Display", display = display.none)
string vChartMode = input.string("Map", "Chart density", options = ["Map", "Clean", "Context", "Audit", "Off"], group = "Display", tooltip = "Map: complete seven-level map plus fixed plan and episode markers. Clean: selected plan/trigger only. Context: map and optional middle band. Audit: adds bounded event observations. Off hides drawings only.", display = display.none)
string vScale = input.string("Medium", "Panel & chart tag size", options = ["Small", "Medium", "Large"], group = "Display", active = CAM_IS_LAB or vBoardMode != "Off" or vDetail != "Off" or vChartMode != "Off", tooltip = "Also controls Strategy Lab readout size.", display = display.none)
string vFocusCode = input.string("Auto", "Inspect play", options = ["Auto", "HA", "LA", "HB", "LB", "HC", "LC", "HD", "LD", "HE", "LE", "HF", "LF", "HG", "LG"], group = "Display", active = vDetail != "Off", tooltip = "Auto: active, armed, approaching, then candidate, with fixed-code ties. A manual code changes only the Inspector. H/L describes context, not trade direction.", display = display.none)
bool vCodes = input.bool(false, "Show play codes", group = "Display", display = display.none)
string vBoardCorner = input.string("Top Left", "Trade HUD position", options = ["Top Left", "Top Right", "Bottom Left", "Bottom Right"], group = "Display · placement", active = vBoardMode != "Off", display = display.none)
string vFocusCorner = input.string("Bottom Left", "Inspector position", options = ["Top Left", "Top Right", "Bottom Left", "Bottom Right"], group = "Display · placement", active = vDetail != "Off", tooltip = "Audit reserves its entire column, so the Inspector moves to the other side. Other HUD modes use the free corner on the same side first. Hidden panels reserve no anchor.", display = display.none)
int vHeaderClearance = input.int(12, "Top Left header clearance · %", minval = 0, maxval = 20, group = "Display · placement", tooltip = "Transparent space beneath the native legend; zero if hidden. Bottom Left has a transparent footer above the watermark. These are layout heuristics.", display = display.none)
bool vPriceTags = input.bool(true, "Price tags", group = "Display · chart", active = vChartMode != "Off", tooltip = "Displaced tags keep true-price leaders and unchanged values. Tracked references receive first priority.", display = display.none)
bool vMiddleBand = input.bool(false, "Camarilla middle band", group = "Display · chart", active = vChartMode == "Context" or vChartMode == "Audit", tooltip = "L2–H2 Camarilla band, not floor-pivot CPR.", display = display.none)
bool vPlanZones = input.bool(true, "Reference risk / reward zones", group = "Display · chart", active = vChartMode != "Off", tooltip = "Frozen reference geometry, not fills or returns. Ended geometry stops at its recorded end time.", display = display.none)
int vHistory = input.int(2, "Ended plans shown", minval = 0, maxval = 8, group = "Display · chart", active = vChartMode != "Off", tooltip = "Newest ended snapshots within visible time and nearby price context. Historical prices are frozen. Zero hides ended geometry.", display = display.none)
int vTagCap = input.int(12, "Chart tag budget", minval = 3, maxval = 24, group = "Display · chart", active = vChartMode != "Off" and vPriceTags, tooltip = "Tracked entry, stop and target come first. Default covers the seven map tags plus tracked references. Episode markers use a separate bounded pool.", display = display.none)
string vTagSide = input.string("Right", "Price tag side", options = ["Right", "Left"], group = "Display · chart", active = vChartMode != "Off" and vPriceTags, tooltip = "Right aligns solid price tags beside the current chart. Left restores interior labels. Colliding labels retain their printed prices and true-price leaders.", display = display.none)
bool vEpisodeMarks = input.bool(true, "Entry / ending markers", group = "Display · chart", active = vChartMode != "Off", tooltip = "Local arrows mark confirmed reference entries. Ending labels show TARGET, STOP or EXPIRED at the recorded observation, not broker fills. A ? suffix discloses ambiguous OHLC paths.", display = display.none)
int vZoneOpacity = input.int(14, "Plan zone opacity · %", minval = 4, maxval = 35, group = "Display · chart", active = vChartMode != "Off" and vPlanZones, tooltip = "Visual contrast only. It does not change price references, signal logic or results.", display = display.none)
bool vLight = vTheme == "Accessible Light"
color vBg = vLight ? color.rgb(246, 248, 252) : vTheme == "Royal Gold" ? color.rgb(21, 23, 28) : color.rgb(16, 24, 38)
color vStripe = vLight ? color.rgb(233, 239, 247) : vTheme == "Royal Gold" ? color.rgb(32, 35, 43) : color.rgb(23, 35, 52)
color vHeader = vLight ? color.rgb(220, 230, 242) : vTheme == "Royal Gold" ? color.rgb(46, 43, 36) : color.rgb(30, 47, 68)
color vText = vLight ? color.rgb(24, 36, 53) : color.rgb(236, 243, 251)
color vMuted = vLight ? color.rgb(82, 98, 118) : color.rgb(168, 184, 204)
color vAccent = vTheme == "Trading Dark" ? color.rgb(105, 164, 255) : vTheme == "Royal Gold" ? color.rgb(226, 197, 137) : vTheme == "Aurora" ? color.rgb(190, 187, 237) : vLight ? color.rgb(39, 68, 100) : color.rgb(205, 225, 246)
color vUp = vTheme == "Trading Dark" ? color.rgb(66, 190, 133) : vLight ? color.rgb(0, 105, 165) : vTheme == "Aurora" ? color.rgb(95, 207, 239) : color.rgb(92, 184, 239)
color vDown = vTheme == "Trading Dark" ? color.rgb(240, 101, 119) : vLight ? color.rgb(151, 85, 0) : color.rgb(237, 171, 76)
color vFrame = vLight ? color.rgb(184, 200, 221) : color.rgb(53, 71, 93)
int vBodySize = vScale == "Small" ? 12 : vScale == "Large" ? 16 : 14
int vSmallSize = vScale == "Small" ? 10 : vScale == "Large" ? 14 : 12
int vTitleSize = vScale == "Small" ? 14 : vScale == "Large" ? 18 : 16
int vTagSize = vScale == "Small" ? 10 : vScale == "Large" ? 14 : 12
f_vWrap(string value, int width, int maxLines) =>
string result = ""
string current = ""
int rows = 1
bool truncated = false
for word in str.split(value, " ")
if not truncated
if str.length(current) + str.length(word) + 1 > width and str.length(current) > 0
if rows < maxLines
result += current + "\n"
current := word
rows += 1
else
truncated := true
else
current += (str.length(current) > 0 ? " " : "") + word
result + current + (truncated ? "…" : "")
f_vPosition(string choice) =>
switch choice
"Top Left" => position.top_left
"Bottom Left" => position.bottom_left
"Bottom Right" => position.bottom_right
"Top Center" => position.top_center
"Bottom Center" => position.bottom_center
"Middle Left" => position.middle_left
"Middle Right" => position.middle_right
"Middle Center" => position.middle_center
=> position.top_right
// Public view API for the Lab tail: occupied anchors include the entire column
// of a tall Audit HUD, so downstream panels cannot overlap its fourteen rows.
f_vFree(array<string> used, string preferred) =>
string result = preferred
if array.includes(used, result)
string paired = preferred == "Top Left" ? "Bottom Left" : preferred == "Bottom Left" ? "Top Left" : preferred == "Top Right" ? "Bottom Right" : "Top Right"
string across = preferred == "Top Left" ? "Top Right" : preferred == "Bottom Left" ? "Bottom Right" : preferred == "Top Right" ? "Top Left" : "Bottom Left"
array<string> order = array.from(paired, across, "Top Left", "Bottom Left", "Top Right", "Bottom Right", "Middle Left", "Middle Right", "Top Center", "Bottom Center", "Middle Center")
for candidate in order
if not array.includes(used, candidate)
result := candidate
break
array.push(used, result)
result
f_vReserveColumn(array<string> used, string anchor) =>
bool left = str.contains(anchor, "Left")
array<string> column = left ? array.from("Top Left", "Bottom Left", "Middle Left") : array.from("Top Right", "Bottom Right", "Middle Right")
for reserved in column
if not array.includes(used, reserved)
array.push(used, reserved)
true
var array<string> vUsedAnchors = array.new<string>()
var string vBoardAnchor = ""
var string vFocusAnchor = ""
if barstate.isfirst
if vBoardMode != "Off"
vBoardAnchor := f_vFree(vUsedAnchors, vBoardCorner)
if vBoardMode == "Audit"
f_vReserveColumn(vUsedAnchors, vBoardAnchor)
if vDetail != "Off"
vFocusAnchor := f_vFree(vUsedAnchors, vFocusCorner)
f_vTime(int stamp) =>
na(stamp) ? "—" : str.format_time(stamp, "MM-dd HH:mm", "UTC")
f_vDate(int stamp) =>
na(stamp) ? "—" : str.format_time(stamp, "yyyy-MM-dd", "UTC")
f_vClock(int stamp) =>
na(stamp) ? "—" : str.format_time(stamp, "HH:mm", "UTC")
f_vSourceShort(string value) =>
str.contains(value, "SYNTHETIC") ? "TEST RTH" : str.contains(value, "RTH") ? "RTH" : str.contains(value, "ETH") ? "ETH" : str.contains(value, "daily") ? "Daily" : str.contains(value, "Selected") ? "Custom" : "Waiting"
f_vClockShort(string value) =>
str.contains(value, "SYNTHETIC") ? "TEST" : str.contains(value, "Exchange") ? "Exchange day" : str.contains(value, "cash") ? "US cash" : "Custom"
f_vInk(color tint) =>
color.r(tint) * 0.2126 + color.g(tint) * 0.7152 + color.b(tint) * 0.0722 > 105 ? color.rgb(10, 18, 28) : color.white
f_vDirection(int direction) =>
direction == 1 ? "LONG" : direction == -1 ? "SHORT" : "—"
f_vState(Play play, bool inWindow) =>
play.state == "WATCH" ? not inWindow ? "WAIT SESSION" : not camBarOhlcValid ? "DATA WAIT" : not play.candidate ? "BLOCKED" : play.approaching ? "APPROACHING" : "CANDIDATE" : play.state
f_vStateColor(string state, int direction) =>
state == "ACTIVE" ? (direction == 1 ? vUp : vDown) : state == "ARMED" or state == "APPROACHING" ? vAccent : state == "STOP" or state == "INVALIDATED" or state == "REJECTED" ? vDown : state == "TARGET" ? vUp : vMuted
f_vShortName(string family, int direction) =>
switch family
"A" => direction == 1 ? "L3 traverse" : "H3 traverse"
"B" => direction == 1 ? "H4 break & retest" : "L4 break & retest"
"C" => "Opening continuation"
"D" => "Context-opposed continuation"
"E" => direction == 1 ? "L4 failure" : "H4 failure"
"F" => "Outer-tier rejection"
"G" => "Opposite-side rejection"
=> "Session guide"
f_vName(Play play) =>
(vCodes ? play.code + " · " : "") + f_vShortName(play.family, play.dir)
f_vSnapshotName(CamPlanSnapshot plan) =>
(vCodes ? plan.code + " · " : "") + f_vShortName(str.substring(plan.code, 1), plan.dir)
f_vAge(Play play) =>
int start = play.state == "ACTIVE" ? play.activationBar : play.state == "ARMED" ? play.setupBar : na
na(start) or na(camClosedBar) ? "" : str.tostring(math.max(0, camClosedBar - start)) + " bars"
f_vTrigger(Play play) =>
na(play.triggerPrice) ? "No close trigger yet" : "close " + play.triggerOperator + " " + f_camReadable(play.triggerPrice)
f_vPlayTip(Play play) =>
play.code + " · " + play.name + "\n" + f_vDirection(play.dir) + " · " + f_vState(play, camInSession) + "\nWHY: " + play.reason + "\nNEXT: " + play.next + "\nFAIL: " + play.invalidation + "\nClose trigger: " + f_vTrigger(play) + "\nSetup / activation UTC: " + f_vTime(play.setupTime) + " / " + f_vTime(play.activationTime)
f_vGateWords(Play play) =>
"Range " + (play.rangeOk ? "PASS" : "BLOCK") + " · Width " + (play.widthOk ? "PASS" : "BLOCK") + "\nOpening " + (play.openOk ? "PASS" : "BLOCK") + " · Region " + (play.regionOk ? "IN" : "OUT") + "\nSMA " + (not smaGate ? "OFF" : play.smaOk ? "PASS" : "BLOCK")
f_vTrackedIndex() =>
int found = -1
if array.size(camPlans) > 0
for i = 0 to array.size(camPlans) - 1
CamPlanSnapshot plan = array.get(camPlans, i)
if plan.state == "ACTIVE"
found := i
break
found
f_vFallbackFocus() =>
int selected = f_camAutoFocus()
if selected < 0 and camInSession and array.size(plays) > 0
for priority = 0 to 2
if selected < 0
for i = 0 to array.size(plays) - 1
Play play = array.get(plays, i)
bool match = priority == 0 ? play.state == "ARMED" : priority == 1 ? play.state == "WATCH" and play.approaching : play.state == "WATCH" and play.candidate
if match
selected := i
break
selected
f_vRows(bool audit) =>
array<int> rows = array.new<int>()
if array.size(plays) > 0
if audit
for i = 0 to array.size(plays) - 1
array.push(rows, i)
else
for priority = 0 to 3
for i = 0 to array.size(plays) - 1
Play play = array.get(plays, i)
bool match = priority == 0 ? play.state == "ACTIVE" : priority == 1 ? play.state == "ARMED" : priority == 2 ? play.state == "WATCH" and play.approaching : play.state == "WATCH" and play.candidate
if match and array.size(rows) < 5 and not array.includes(rows, i)
array.push(rows, i)
rows
f_vRecovery() =>
not camChartSupported ? "Use standard, time-based candles from 1 to 60 minutes." : not camOpenKnown ? "Load the observed decision opening and complete prior source windows." : not camMapReady ? camDataReason : not camInSession ? "Wait for the selected decision window. The last committed map remains inspectable." : "No qualifying setup. Inspect Audit for each prerequisite."
f_vSourceTip() =>
"PUBLIC CONTEXT\nSource: " + camSourceLabel + " · close: " + camCloseLabel + "\n" + camSourceSelectionReason + "\nDecision: " + camDecisionLabel + "\nMap source UTC: " + f_vTime(camMap.sourceStart) + " → " + f_vTime(camMap.sourceEnd) + "\nSelection cutoff: " + f_vTime(camSourceCutoff) + "\nObserved 1m opening: " + f_vTime(camOpeningSourceTime) + " @ " + f_camExact(camOpen) + "\nH/L/C: " + f_camExact(camMap.h) + " / " + f_camExact(camMap.l) + " / " + f_camExact(camMap.c) + "\n" + camDataReason
f_vRiskTip() =>
"ORIGINAL EXECUTION\nMinimum R: " + str.tostring(minRewardR) + " · buffer " + str.tostring(stopBufferTicks) + " ticks\nStop: " + stopModel + " · ATR " + str.tostring(stopAtrMultiple) + "\nTarget: " + targetModel + " · ATR " + str.tostring(targetAtrMultiple) + "\nMax target ATR: " + (maxTargetAtr == 0 ? "OFF" : str.tostring(maxTargetAtr)) + "\nSetup / active limits: " + str.tostring(setupDeadline) + " / " + str.tostring(activeDeadline) + " bars\nRearm: " + (allowRearm ? "ON · cooldown " + str.tostring(cooldownBars) + " · cap " + str.tostring(maxEpisodes) : "OFF") + "\nChart SMA: " + (smaGate ? str.tostring(smaLength) : "OFF") + "\nReference plans, not broker fills or a validated edge."
type CamViewRow
string key = ""
string value = ""
string tip = ""
int kind = 0
f_vAdd(array<CamViewRow> rows, string key, string value, string tip, int kind = 0) =>
array.push(rows, CamViewRow.new(key, value, tip, kind))
f_vSignature(array<CamViewRow> rows) =>
string result = ""
for row in rows
result += str.tostring(row.kind)
result
f_vInsetStart(array<CamViewRow> rows, string anchor) =>
if anchor == "Top Left" and vHeaderClearance > 0
f_vAdd(rows, "", "", "Transparent chart-header clearance", 5)
f_vInsetEnd(array<CamViewRow> rows, string anchor) =>
if anchor == "Bottom Left"
f_vAdd(rows, "", "", "Transparent watermark clearance", 5)
f_vRender(table panel, array<CamViewRow> rows, string anchor) =>
table.clear(panel, 0, 0, 1, array.size(rows) - 1)
for i = 0 to array.size(rows) - 1
CamViewRow row = array.get(rows, i)
color background = row.kind == 5 ? color.new(vBg, 100) : row.kind == 2 ? vHeader : row.kind == 4 ? vStripe : vBg
if row.kind == 1
table.cell(panel, 0, i, row.key, text_color = vMuted, bgcolor = background, text_size = vSmallSize, text_halign = text.align_left, text_font_family = font.family_default, tooltip = row.tip)
table.cell(panel, 1, i, row.value, text_color = vText, bgcolor = background, text_size = vBodySize, text_halign = text.align_right, text_font_family = font.family_monospace, text_formatting = text.format_bold, tooltip = row.tip)
else
// clear() may remove merges: reapply before every full-row write.
table.merge_cells(panel, 0, i, 1, i)
table.cell(panel, 0, i, row.value, height = row.kind == 5 ? (anchor == "Top Left" ? (vBoardMode == "Audit" ? math.min(vHeaderClearance, 8) : vHeaderClearance) : 5) : 0, text_color = row.kind == 3 ? vMuted : row.kind == 2 or row.kind == 4 ? vAccent : vText, bgcolor = background, text_size = row.kind == 2 ? vTitleSize : row.kind == 3 ? vSmallSize : vBodySize, text_halign = text.align_left, text_font_family = font.family_default, text_formatting = row.kind == 2 or row.kind == 4 ? text.format_bold : text.format_none, tooltip = row.tip)
// Setter pools survive ordinary price/text changes; surplus handles are deleted.
f_vLine(array<line> pool, int slot, int x1, float y1, int x2, float y2, color tint, string style, int width = 1) =>
line item = na
if slot < array.size(pool)
item := array.get(pool, slot)
line.set_xy1(item, x1, y1)
line.set_xy2(item, x2, y2)
line.set_color(item, tint)
line.set_style(item, style)
line.set_width(item, width)
else
item := line.new(x1, y1, x2, y2, xloc = xloc.bar_time, color = tint, style = style, width = width)
array.push(pool, item)
item
f_vLabel(array<label> pool, int slot, int stamp, float price, string value, color tint, string tip, bool faceLeft = false, bool solid = false) =>
label item = na
if slot < array.size(pool)
item := array.get(pool, slot)
label.set_xy(item, stamp, price)
label.set_text(item, value)
label.set_color(item, solid ? tint : vBg)
label.set_textcolor(item, solid ? f_vInk(tint) : tint)
label.set_size(item, vTagSize)
label.set_style(item, faceLeft ? label.style_label_right : label.style_label_left)
label.set_tooltip(item, tip)
else
item := label.new(stamp, price, value, xloc = xloc.bar_time, style = faceLeft ? label.style_label_right : label.style_label_left, color = solid ? tint : vBg, textcolor = solid ? f_vInk(tint) : tint, size = vTagSize, text_font_family = font.family_default, tooltip = tip)
array.push(pool, item)
item
f_vMarker(array<label> pool, int slot, int stamp, float price, string value, color tint, string tip, string style) =>
label item = f_vLabel(pool, slot, stamp, price, value, tint, tip, false, true)
label.set_style(item, style)
item
f_vBox(array<box> pool, int slot, int left, float top, int right, float bottom, color tint) =>
box item = na
if slot < array.size(pool)
item := array.get(pool, slot)
box.set_lefttop(item, left, top)
box.set_rightbottom(item, right, bottom)
box.set_bgcolor(item, tint)
box.set_border_color(item, color.new(tint, 100))
else
item := box.new(left, top, right, bottom, xloc = xloc.bar_time, bgcolor = tint, border_color = color.new(tint, 100))
array.push(pool, item)
item
f_vTagY(array<float> used, float price, float gap) =>
float result = price
for attempt = 0 to 24
float candidate = attempt == 0 ? price : price + (attempt % 2 == 1 ? 1 : -1) * math.ceil(attempt / 2.0) * gap
bool clear = true
for previous in used
if math.abs(candidate - previous) < gap * 0.95
clear := false
if clear
result := candidate
break
array.push(used, result)
result
f_vExtent(array<float> bounds, float price) =>
if not na(price)
array.set(bounds, 0, math.min(array.get(bounds, 0), price))
array.set(bounds, 1, math.max(array.get(bounds, 1), price))
true
f_vTagGap(float renderedRange, float tick, int fontPixels) =>
// Bounded display heuristic: map/history lines also expand autoscale. Pine
// exposes no pane pixel height, so this is not a universal collision proof.
float fraction = math.min(0.07, math.max(0.035, fontPixels * 0.004))
math.max(renderedRange * fraction, tick * 4)
float vFallbackRange = ta.highest(high[1], 80) - ta.lowest(low[1], 80)
var float vVisibleHigh = na
var float vVisibleLow = na
if time == chart.left_visible_bar_time
vVisibleHigh := high
vVisibleLow := low
else if time > chart.left_visible_bar_time and time <= chart.right_visible_bar_time
vVisibleHigh := math.max(nz(vVisibleHigh, high), high)
vVisibleLow := math.min(nz(vVisibleLow, low), low)
var array<line> vLines = array.new<line>()
var array<label> vLabels = array.new<label>()
var array<label> vMarkers = array.new<label>()
var array<box> vBoxes = array.new<box>()
var table vBoard = na
var table vFocus = na
var string vBoardSignature = ""
var string vFocusSignature = ""
// VIEW RENDER — exact fixture insertion boundary; no production markers precede it.
if (barstate.islastconfirmedhistory or barstate.isrealtime) and (vBoardMode != "Off" or vDetail != "Off" or vChartMode != "Off")
int vTrackIndex = f_vTrackedIndex()
bool vTracking = vTrackIndex >= 0
CamPlanSnapshot vTracked = vTracking ? array.get(camPlans, vTrackIndex) : CamPlanSnapshot.new()
int vNextIndex = f_vFallbackFocus()
Play vUpcoming = vNextIndex >= 0 ? array.get(plays, vNextIndex) : Play.new()
int vInspectIndex = vFocusCode == "Auto" ? vNextIndex : f_camCodeIndex(vFocusCode)
bool vHasUpcoming = vNextIndex >= 0 and not vTracking
string vSourceTip = f_vSourceTip()
string vRiskTip = f_vRiskTip()
string vClosedTip = "Last committed chart close, UTC: " + f_vTime(camClosedTime) + "\nPrice: " + f_camExact(camClosedPrice) + "\nForming quotes do not update this value."
int vBarMs = int(timeframe.in_seconds() * 1000)
int vNow = na(camClosedTime) ? time : camClosedTime
int vRight = vNow + vBarMs * 4
int vLeft = nz(chart.left_visible_bar_time, vNow - vBarMs * 60)
int vVisibleRight = math.min(vNow, nz(chart.right_visible_bar_time, vNow))
bool vLeftPanel = array.includes(vUsedAnchors, "Top Left") or array.includes(vUsedAnchors, "Bottom Left") or array.includes(vUsedAnchors, "Middle Left")
int vTagTime = vTagSide == "Right" ? vRight : vLeft + int(math.max(0, vVisibleRight - vLeft) * (vLeftPanel ? 0.45 : 0.12))
float vRange = math.max(nz(vVisibleHigh - vVisibleLow, nz(vFallbackRange, syminfo.mintick * 100)), syminfo.mintick * 20)
int vLineCount = 0
int vLabelCount = 0
int vMarkerCount = 0
int vBoxCount = 0
int vHistoryShown = 0
int vHistoryOmitted = 0
int vContextOmitted = 0
array<float> vOccupied = array.new<float>()
bool vCanMap = camChartSupported and camMapReady and camMap.valid and not na(camSessionStart) and vChartMode != "Off"
float vFocusLevel = vTracking ? vTracked.triggerPrice : vHasUpcoming ? vUpcoming.triggerPrice : na
int vRoleDirection = vTracking ? vTracked.dir : vHasUpcoming ? vUpcoming.dir : 0
// Admit before drawing. Candle range still controls the existing history
// proximity filter; the wider rendered extent controls label spacing only.
array<float> vExtent = array.from(nz(vVisibleLow, nz(camClosedPrice, close) - vRange * 0.5), nz(vVisibleHigh, nz(camClosedPrice, close) + vRange * 0.5))
array<float> vMapLevels = array.from(camMap.l6, camMap.l4, camMap.l3, camMap.c, camMap.h3, camMap.h4, camMap.h6)
array<bool> vMapVisible = array.new_bool(7, false)
array<int> vHistoryIndices = array.new_int()
array<int> vAuditIndices = array.new_int()
int vReservedTags = 0
if vChartMode != "Off" and vTracking
for price in array.from(vTracked.entry, vTracked.stop, vTracked.target)
f_vExtent(vExtent, price)
vReservedTags := vPriceTags ? 3 : 0
if vCanMap
for pass = 0 to 1
for j = 0 to 6
float price = array.get(vMapLevels, j)
bool focused = not na(vFocusLevel) and f_camCompare(price, vFocusLevel) == 0
bool visible = not na(price) and (vChartMode != "Clean" or focused or (not vTracking and not vHasUpcoming and j == 3))
if visible and ((pass == 0 and focused) or (pass == 1 and not focused))
array.set(vMapVisible, j, true)
f_vExtent(vExtent, price)
if vPriceTags and vReservedTags < vTagCap
vReservedTags += 1
if vMiddleBand and (vChartMode == "Context" or vChartMode == "Audit")
f_vExtent(vExtent, camMap.l2)
f_vExtent(vExtent, camMap.h2)
if vChartMode != "Off" and array.size(camPlans) > 0
for i = 0 to array.size(camPlans) - 1
CamPlanSnapshot plan = array.get(camPlans, i)
if plan.state != "ACTIVE" and not na(plan.endTime) and not na(plan.activationTime)
bool timeVisible = plan.endTime >= vLeft and plan.activationTime <= vVisibleRight
bool priceNear = math.max(plan.entry, math.max(plan.stop, plan.target)) <= nz(vVisibleHigh, plan.entry) + vRange and math.min(plan.entry, math.min(plan.stop, plan.target)) >= nz(vVisibleLow, plan.entry) - vRange
if timeVisible and priceNear and array.size(vHistoryIndices) < vHistory
array.push(vHistoryIndices, i)
for price in array.from(plan.entry, plan.stop, plan.target)
f_vExtent(vExtent, price)
if vEpisodeMarks
f_vExtent(vExtent, nz(plan.outcomePrice, plan.entry))
if vPriceTags and not vEpisodeMarks and vReservedTags < vTagCap
vReservedTags += 1
else
vHistoryOmitted += 1
if vChartMode == "Audit" and vPriceTags and array.size(camEvents) > 0
for i = 0 to math.min(3, array.size(camEvents) - 1)
CamEvent event = array.get(camEvents, i)
if vReservedTags < vTagCap and event.eventTime >= vLeft and event.eventTime <= vVisibleRight and not na(event.price)
array.push(vAuditIndices, i)
f_vExtent(vExtent, event.price)
vReservedTags += 1
float vRenderedRange = math.max(array.get(vExtent, 1) - array.get(vExtent, 0), syminfo.mintick * 20)
float vGap = f_vTagGap(vRenderedRange, syminfo.mintick, vTagSize)
// Reserve marker text rows before right/left-column price tags. A local
// ending can share H4/L3 exactly; displace only the tag with its true leader.
if vChartMode != "Off" and vEpisodeMarks
if vTracking and vTracked.activationTime >= vLeft
array.push(vOccupied, vTracked.entry + (vTracked.dir == 1 ? -0.6 : 0.6) * vGap)
for i in vHistoryIndices
CamPlanSnapshot plan = array.get(camPlans, i)
if plan.activationTime >= vLeft
array.push(vOccupied, plan.entry + (plan.dir == 1 ? -0.6 : 0.6) * vGap)
array.push(vOccupied, nz(plan.outcomePrice, plan.state == "STOP" ? plan.stop : plan.state == "TARGET" ? plan.target : plan.entry))
// Manual vInspectIndex cannot replace this immutable tracked snapshot.
if vChartMode != "Off" and vTracking
array<float> prices = array.from(vTracked.entry, vTracked.stop, vTracked.target)
array<string> names = array.from("PLAN ENTRY REF", "PLAN STOP", "PLAN TARGET")
array<color> colors = array.from(vAccent, vDown, vUp)
for j = 0 to 2
float price = array.get(prices, j)
color tint = array.get(colors, j)
f_vLine(vLines, vLineCount, vTracked.activationTime, price, vRight, price, tint, line.style_solid, j == 0 ? 2 : 1)
vLineCount += 1
if vTagTime < vTracked.activationTime
f_vLine(vLines, vLineCount, vTagTime, price, vTracked.activationTime, price, color.new(tint, 45), line.style_dotted)
vLineCount += 1
if vPriceTags and vLabelCount < vTagCap
float tagY = f_vTagY(vOccupied, price, vGap)
string tip = "Tracked " + vTracked.code + " · episode " + str.tostring(vTracked.episode) + "\n" + array.get(names, j) + " " + f_camExact(price) + "\nFrozen at " + f_vTime(vTracked.activationTime) + " UTC. Dotted left extension is a present reference guide, not historical availability."
f_vLabel(vLabels, vLabelCount, vTagTime, tagY, array.get(names, j) + " " + f_camPrice(price), tint, tip, vTagSide == "Right", vTagSide == "Right")
vLabelCount += 1
if tagY != price
f_vLine(vLines, vLineCount, vTagTime, price, vTagTime, tagY, color.new(tint, 40), line.style_dotted)
vLineCount += 1
if vPlanZones
f_vBox(vBoxes, vBoxCount, vTracked.activationTime, math.max(vTracked.entry, vTracked.stop), vRight, math.min(vTracked.entry, vTracked.stop), color.new(vDown, 100 - vZoneOpacity))
vBoxCount += 1
f_vBox(vBoxes, vBoxCount, vTracked.activationTime, math.max(vTracked.entry, vTracked.target), vRight, math.min(vTracked.entry, vTracked.target), color.new(vUp, 100 - vZoneOpacity))
vBoxCount += 1
if vEpisodeMarks and vTracked.activationTime >= vLeft and vTracked.activationTime <= vVisibleRight
string tip = "REFERENCE ENTRY · " + vTracked.code + " · episode " + str.tostring(vTracked.episode) + "\nConfirmed " + f_vTime(vTracked.activationTime) + " UTC @ " + f_camExact(vTracked.entry) + "\nFrozen stop / target " + f_camExact(vTracked.stop) + " / " + f_camExact(vTracked.target) + "\n" + vTracked.reason + "\nNot a broker fill or a fresh entry at the latest quote."
f_vMarker(vMarkers, vMarkerCount, vTracked.activationTime, vTracked.entry, vTracked.code + (vTracked.dir == 1 ? " ▲ ENTRY" : " ▼ ENTRY"), vTracked.dir == 1 ? vUp : vDown, tip, vTracked.dir == 1 ? label.style_label_up : label.style_label_down)
vMarkerCount += 1
// Role color belongs to the relevant play, never the current quote's position.
if vCanMap
array<string> codes = array.from("L6", "L4", "L3", "PC", "H3", "H4", "H6")
for pass = 0 to 1
for j = 0 to 6
float price = array.get(vMapLevels, j)
bool focused = not na(vFocusLevel) and f_camCompare(price, vFocusLevel) == 0
bool visible = array.get(vMapVisible, j)
if visible and ((pass == 0 and focused) or (pass == 1 and not focused))
color tint = focused and vRoleDirection != 0 ? (vRoleDirection == 1 ? vUp : vDown) : j < 3 ? vUp : j > 3 ? vDown : vAccent
f_vLine(vLines, vLineCount, camSessionStart, price, vRight, price, color.new(tint, focused ? 0 : vChartMode == "Map" ? 20 : 50), vChartMode == "Map" or focused ? line.style_solid : line.style_dotted, focused or j == 2 or j == 4 ? 2 : 1)
vLineCount += 1
if vPriceTags
if vLabelCount < vTagCap
float tagY = f_vTagY(vOccupied, price, vGap)
string tip = "CURRENT MAP · " + array.get(codes, j) + " " + f_camExact(price) + "\nMap usable at decision opening " + f_vTime(camSessionStart) + " UTC. A dotted left guide is a current reference only.\n" + vSourceTip
f_vLabel(vLabels, vLabelCount, vTagTime, tagY, (focused ? "TRIGGER " : "") + array.get(codes, j) + " " + f_camPrice(price), tint, tip, vTagSide == "Right", vTagSide == "Right")
vLabelCount += 1
if vTagTime < camSessionStart
f_vLine(vLines, vLineCount, vTagTime, price, camSessionStart, price, color.new(tint, 70), line.style_dotted)
vLineCount += 1
if tagY != price
f_vLine(vLines, vLineCount, vTagTime, price, vTagTime, tagY, color.new(tint, 45), line.style_dotted)
vLineCount += 1
else
vContextOmitted += 1
if vMiddleBand and (vChartMode == "Context" or vChartMode == "Audit")
f_vBox(vBoxes, vBoxCount, camSessionStart, camMap.h2, vRight, camMap.l2, color.new(vAccent, 96))
vBoxCount += 1
// Ended geometry uses its own snapshot and stops exactly at its endTime.
if array.size(vHistoryIndices) > 0
for i in vHistoryIndices
CamPlanSnapshot plan = array.get(camPlans, i)
if plan.state != "ACTIVE"
if vHistoryShown < vHistory
vHistoryShown += 1
array<float> prices = array.from(plan.entry, plan.stop, plan.target)
for price in prices
color endedTint = price == plan.stop ? vDown : price == plan.target ? vUp : vAccent
f_vLine(vLines, vLineCount, plan.activationTime, price, plan.endTime, price, color.new(endedTint, 35), line.style_dashed)
vLineCount += 1
if vPlanZones
f_vBox(vBoxes, vBoxCount, plan.activationTime, math.max(plan.entry, plan.stop), plan.endTime, math.min(plan.entry, plan.stop), color.new(vDown, 100 - math.max(4, vZoneOpacity - 4)))
vBoxCount += 1
f_vBox(vBoxes, vBoxCount, plan.activationTime, math.max(plan.entry, plan.target), plan.endTime, math.min(plan.entry, plan.target), color.new(vUp, 100 - math.max(4, vZoneOpacity - 4)))
vBoxCount += 1
if vEpisodeMarks
string markerTip = "REFERENCE EPISODE · " + plan.code + " #" + str.tostring(plan.episode) + "\n" + f_vTime(plan.activationTime) + " → " + f_vTime(plan.endTime) + " UTC\nFrozen entry / stop / target " + f_camExact(plan.entry) + " / " + f_camExact(plan.stop) + " / " + f_camExact(plan.target) + "\n" + plan.reason + (plan.ambiguous ? "\nAmbiguous OHLC path; stop-first reference convention." : "") + "\nThese are observations, not broker fills."
if plan.activationTime >= vLeft
f_vMarker(vMarkers, vMarkerCount, plan.activationTime, plan.entry, plan.code + (plan.dir == 1 ? " ▲ ENTRY" : " ▼ ENTRY"), plan.dir == 1 ? vUp : vDown, markerTip, plan.dir == 1 ? label.style_label_up : label.style_label_down)
vMarkerCount += 1
float markerPrice = nz(plan.outcomePrice, plan.state == "STOP" ? plan.stop : plan.state == "TARGET" ? plan.target : plan.entry)
string ending = plan.code + (plan.state == "TARGET" ? " ✓ " : plan.state == "STOP" ? " × " : " · ") + plan.state + (plan.ambiguous ? " ?" : "")
string endingTip = na(plan.outcomePrice) ? "Unpriced ending; marker anchored at frozen reference " + f_camExact(markerPrice) + " for orientation. No exit price or fill inferred." : "Observed end reference " + f_camExact(markerPrice)
f_vMarker(vMarkers, vMarkerCount, plan.endTime, markerPrice, ending, plan.state == "TARGET" ? vUp : plan.state == "STOP" ? vDown : vMuted, markerTip + "\n" + endingTip, label.style_label_left)
vMarkerCount += 1
if vPriceTags and not vEpisodeMarks and vLabelCount < vTagCap
float tagY = f_vTagY(vOccupied, plan.entry, vGap)
string tip = "ENDED " + plan.code + " · episode " + str.tostring(plan.episode) + "\n" + f_vTime(plan.activationTime) + " → " + f_vTime(plan.endTime) + " UTC\nFrozen entry / stop / target: " + f_camExact(plan.entry) + " / " + f_camExact(plan.stop) + " / " + f_camExact(plan.target) + "\n" + plan.reason + (plan.ambiguous ? "\nSame-bar ambiguity: stop-first reference policy." : "")
f_vLabel(vLabels, vLabelCount, plan.endTime, tagY, "ENDED " + f_vDirection(plan.dir) + " · " + plan.state, vMuted, tip, true)
vLabelCount += 1
if tagY != plan.entry
f_vLine(vLines, vLineCount, plan.endTime, plan.entry, plan.endTime, tagY, color.new(vMuted, 65), line.style_dotted)
vLineCount += 1
if array.size(vAuditIndices) > 0
for i in vAuditIndices
CamEvent event = array.get(camEvents, i)
if vLabelCount < vTagCap and event.eventTime >= vLeft and event.eventTime <= vVisibleRight and not na(event.price)
float tagY = f_vTagY(vOccupied, event.price, vGap)
f_vLabel(vLabels, vLabelCount, event.eventTime, tagY, event.code + " " + event.kind, vMuted, event.reason + "\nObserved " + f_vTime(event.eventTime) + " UTC · not a broker fill", true)
vLabelCount += 1
if tagY != event.price
f_vLine(vLines, vLineCount, event.eventTime, event.price, event.eventTime, tagY, color.new(vMuted, 65), line.style_dotted)
vLineCount += 1
while array.size(vLines) > vLineCount
line.delete(array.pop(vLines))
while array.size(vLabels) > vLabelCount
label.delete(array.pop(vLabels))
while array.size(vMarkers) > vMarkerCount
label.delete(array.pop(vMarkers))
while array.size(vBoxes) > vBoxCount
box.delete(array.pop(vBoxes))
if vBoardMode != "Off"
array<CamViewRow> rows = array.new<CamViewRow>()
f_vInsetStart(rows, vBoardAnchor)
string vRibbonContext = f_vSourceShort(camSourceLabel) + " · " + (not camMapReady ? "CONTEXT WAIT" : camBias == 1 ? "HIGHER" : camBias == -1 ? "LOWER" : "NEUTRAL") + (camMapReady ? " · " + str.upper(camWidthLabel) : "")
string vAtrShort = str.contains(camAtrBasis, "RTH") ? "RTH14" : str.contains(camAtrBasis, "Provider") ? "Daily14" : "WAIT"
string vAtrValue = camAtrReady ? f_camPrice(camAtrReference) : str.tostring(camAtrSamples) + "/14 WAIT"
string vAtrTip = "ATR basis: " + camAtrBasis + "\nFrozen value " + f_camExact(camAtrReference) + " at " + f_vTime(camAtrStamp) + " UTC\nObserved chain start " + f_vTime(camAtrStart) + " · samples " + str.tostring(camAtrSamples) + "\nRTH counts completed observed sessions; entirely absent dates may be closures or unavailable feed. No provider fallback.\n" + vRiskTip
if vBoardMode == "Ribbon"
f_vAdd(rows, "", vRibbonContext, "CAMARILLA DESK " + CAM_VERSION + " · " + syminfo.tickerid + "\n" + vSourceTip, 4)
string status = vTracking ? vTracked.code + " " + f_vDirection(vTracked.dir) + " · ACTIVE" + (camActiveCount > 1 ? " +" + str.tostring(camActiveCount - 1) : "") : vHasUpcoming ? vUpcoming.code + " " + f_vDirection(vUpcoming.dir) + " · " + f_vState(vUpcoming, camInSession) : not camMapReady ? "DATA WAIT" : not camInSession ? "WAIT SESSION" : "NO QUALIFYING SETUP"
string statusTip = vTracking ? f_vSnapshotName(vTracked) + "\nConfirmed " + f_vTime(vTracked.activationTime) + " UTC\nEntry / stop / target " + f_camExact(vTracked.entry) + " / " + f_camExact(vTracked.stop) + " / " + f_camExact(vTracked.target) + "\n" + vTracked.reason : vHasUpcoming ? f_vPlayTip(vUpcoming) : f_vRecovery()
f_vAdd(rows, "", status, statusTip + "\n" + str.tostring(camActiveCount) + " active · " + str.tostring(camApproachingCount) + " approaching · " + str.tostring(camCandidateCount) + " candidates. These counts overlap. Expand Clean/Context/Audit for detail.", 2)
string next = vTracking ? "NEXT · Stop / target · " + str.tostring(math.max(0, activeDeadline - (camClosedBar - vTracked.activationBar))) + "b max" : vHasUpcoming ? vUpcoming.state == "ARMED" ? "NEXT · " + f_vTrigger(vUpcoming) : vUpcoming.approaching ? "NEXT · Wait for setup" : "NEXT · Wait for approach" : not camMapReady ? "NEXT · Check source / ATR readiness" : not camInSession ? "NEXT · Wait for " + f_vClockShort(camDecisionLabel) : "NEXT · Inspect Audit for prerequisites"
f_vAdd(rows, "", next, statusTip + "\n" + (vTracking ? "Session end can precede the displayed bar deadline. References are not broker fills." : vHasUpcoming ? vUpcoming.next : f_vRecovery()), 3)
f_vAdd(rows, "", "ATR " + vAtrShort + " " + vAtrValue + " · CLOSED " + f_vClock(camClosedTime), vAtrTip + "\n" + vClosedTip, 3)
else
f_vAdd(rows, "", "CAMARILLA DESK " + CAM_VERSION + " · " + syminfo.ticker, syminfo.tickerid + " · " + timeframe.period + "\n" + vSourceTip, 3)
if vTracking
f_vAdd(rows, "", f_vWrap(f_vDirection(vTracked.dir) + " · " + f_vSnapshotName(vTracked), 31, 2), vTracked.reason, 2)
int age = math.max(0, camClosedBar - vTracked.activationBar)
f_vAdd(rows, "", "ACTIVE · " + str.tostring(age) + " bars · confirmed " + f_vClock(vTracked.activationTime) + " UTC", "Frozen activation " + f_vTime(vTracked.activationTime) + " UTC · episode " + str.tostring(vTracked.episode), 4)
f_vAdd(rows, "ENTRY REF", f_camPrice(vTracked.entry), "Observed activation close, not a fill.\n" + f_camExact(vTracked.entry), 1)
f_vAdd(rows, "STOP", f_camPrice(vTracked.stop), "Frozen " + vTracked.stopMode + " stop\n" + f_camExact(vTracked.stop), 1)
f_vAdd(rows, "TARGET", f_camPrice(vTracked.target) + " · " + str.tostring(vTracked.effectiveR, "0.00") + "R", "Frozen " + vTracked.targetMode + " target\n" + f_camExact(vTracked.target), 1)
f_vAdd(rows, "LAST CLOSED", f_camPrice(camClosedPrice) + " · " + f_vClock(camClosedTime), vClosedTip, 1)
f_vAdd(rows, "", "NEXT · stop / target / expiry · " + str.tostring(math.max(0, activeDeadline - age)) + " bars left", "Later completed-bar observations resolve the fixed plan. Session expiry can precede the bar deadline.\n" + vRiskTip, 3)
else if vHasUpcoming
f_vAdd(rows, "", f_vWrap(f_vDirection(vUpcoming.dir) + " · " + f_vName(vUpcoming), 31, 2), f_vPlayTip(vUpcoming), 2)
f_vAdd(rows, "", f_vState(vUpcoming, camInSession) + (f_vAge(vUpcoming) == "" ? "" : " · " + f_vAge(vUpcoming)), f_vPlayTip(vUpcoming), 4)
f_vAdd(rows, "", "CONFIRM · " + f_vTrigger(vUpcoming), "This close also requires the preceding setup sequence.\n" + vUpcoming.next, 0)
f_vAdd(rows, "", "NEXT · " + f_vWrap(vUpcoming.next, 36, 2), vUpcoming.next, 3)
f_vAdd(rows, "LAST CLOSED", f_camPrice(camClosedPrice) + " · " + f_vClock(camClosedTime), vClosedTip, 1)
else
f_vAdd(rows, "", "SESSION GUIDE", "No active plan or eligible setup is selected.", 2)
f_vAdd(rows, "", not camMapReady ? "DATA WAIT" : not camInSession ? "WAIT SESSION" : "NO QUALIFYING SETUP", camDataReason, 4)
f_vAdd(rows, "", f_vWrap(f_vRecovery(), 38, 3), f_vRecovery(), 0)
f_vAdd(rows, "LAST CLOSED", f_camPrice(camClosedPrice) + " · " + f_vClock(camClosedTime), vClosedTip, 1)
if array.size(camPlans) > 0
CamPlanSnapshot last = array.get(camPlans, 0)
if last.state != "ACTIVE"
f_vAdd(rows, "", f_vWrap("LAST ENDED · " + f_vDirection(last.dir) + " " + f_vSnapshotName(last) + " · " + last.state, 38, 2), last.reason + "\nEnd " + f_vTime(last.endTime) + " UTC; not a new setup.", 3)
int otherCount = math.max(0, camActiveCount - (vTracking ? 1 : 0))
if otherCount > 0
string others = ""
int shown = 0
for plan in camPlans
if plan.state == "ACTIVE" and (not vTracking or plan.code != vTracked.code or plan.episode != vTracked.episode or plan.sessionStart != vTracked.sessionStart)
if shown < 2
others += (shown == 0 ? "" : " · ") + f_vDirection(plan.dir) + " " + f_vSnapshotName(plan)
shown += 1
f_vAdd(rows, "", f_vWrap("ALSO ACTIVE · " + others + (otherCount > shown ? " +" + str.tostring(otherCount - shown) : ""), 38, 2), str.tostring(otherCount) + " additional active snapshots. Only the newest active is chart-tracked. Manual inspection cannot replace it.", 3)
f_vAdd(rows, "", str.tostring(camActiveCount) + " active · " + str.tostring(camApproachingCount) + " approaching · " + str.tostring(camCandidateCount) + (camCandidateCount == 1 ? " candidate" : " candidates"), "Approaching is a candidate whose region prerequisite passes. Counts overlap; they are not probabilities.", 3)
if vBoardMode == "Context" or vBoardMode == "Audit"
array<int> attention = f_vRows(vBoardMode == "Audit")
f_vAdd(rows, "", vBoardMode == "Audit" ? "ALL 14 · PREREQUISITE AUDIT" : "ATTENTION · UP TO FIVE PLAYS", "ACTIVE → ARMED → APPROACHING → CANDIDATE; fixed-code ties. Attention, not strength.", 4)
for i in attention
Play play = array.get(plays, i)
string state = f_vState(play, camInSession)
string brief = vBoardMode == "Audit" ? play.code + " · " + f_vDirection(play.dir) + " · " + state : f_vName(play) + " · " + f_vDirection(play.dir) + "\n" + state + (play.state == "ARMED" or play.state == "WATCH" and play.candidate ? " · " + f_vTrigger(play) : "")
f_vAdd(rows, "", brief, f_vName(play) + "\n" + f_vPlayTip(play) + "\n" + f_vGateWords(play), 3)
f_vAdd(rows, "", vRibbonContext, vSourceTip, 4)
f_vAdd(rows, "", "ATR " + vAtrShort + " " + vAtrValue + " · " + f_vClockShort(camDecisionLabel), vAtrTip, 3)
f_vAdd(rows, "", "MIN " + str.tostring(minRewardR, "0.0") + "R · " + str.tostring(stopBufferTicks) + " tick buffer · " + stopModel + " SL", vRiskTip, 3)
if vBoardMode == "Audit"
f_vAdd(rows, "", "CONTEXT " + (camBias == 1 ? "Higher" : camBias == -1 ? "Lower" : "Neutral") + " · WIDTH " + camWidthLabel, "Close delta " + f_camExact(camRangeDelta) + " · neutral threshold " + f_camExact(camRangeThreshold) + "\nGeometric band relation (separate): " + camRelationship + "\nWidth ratio " + f_camExact(camWidthRatio) + "\nSelected ATR " + f_camExact(camAtrReference) + " at " + f_vTime(camAtrStamp) + " UTC", 3)
f_vAdd(rows, "", "REARM " + (allowRearm ? "ON · " + str.tostring(cooldownBars) + "b · cap " + str.tostring(maxEpisodes) : "OFF") + " · TP " + targetModel, vRiskTip, 3)
f_vAdd(rows, "", "REFERENCE PLANS · NOT ORDER FILLS", "Drawings: " + str.tostring(vHistoryShown) + " ended shown / " + str.tostring(vHistoryOmitted) + " omitted; " + str.tostring(vContextOmitted) + " context tags omitted.\n" + (CAM_IS_LAB ? "Lab: alert() calls for reference JSON; order-fill alerts for emulated orders. " : "Static activation alert: desired condition + Once Per Bar Close. ") + "Aggregate JSON: Any alert() function call. Recreate saved alerts after changes. Displaying this recipe does not test delivery.", 3)
f_vInsetEnd(rows, vBoardAnchor)
string signature = f_vSignature(rows)
if na(vBoard) or signature != vBoardSignature
if not na(vBoard)
table.delete(vBoard)
vBoard := table.new(f_vPosition(vBoardAnchor), 2, array.size(rows), bgcolor = color.new(vBg, 100), frame_width = 0, border_width = 0)
vBoardSignature := signature
f_vRender(vBoard, rows, vBoardAnchor)
if vDetail != "Off"
array<CamViewRow> rows = array.new<CamViewRow>()
f_vInsetStart(rows, vFocusAnchor)
if vInspectIndex >= 0 and vInspectIndex < array.size(plays)
Play play = array.get(plays, vInspectIndex)
string tip = f_vPlayTip(play)
f_vAdd(rows, "", "INSPECT · " + f_vName(play), tip, 4)
f_vAdd(rows, "", f_vDirection(play.dir) + " · " + f_vState(play, camInSession) + (f_vAge(play) == "" ? "" : " · " + f_vAge(play)), tip, 2)
f_vAdd(rows, "", "WHY · " + f_vWrap(play.reason, 38, 3), play.reason, 0)
f_vAdd(rows, "", "NEXT · " + f_vWrap(play.next, 38, 3), play.next, 0)
f_vAdd(rows, "", "FAIL · " + f_vWrap(play.invalidation, 38, 2), play.invalidation, 3)
if vDetail == "Standard"
f_vAdd(rows, "", f_vGateWords(play), "Direct core flags. Candidate excludes current region; Approaching includes it. SMA is an optional original close filter.", 3)
f_vAdd(rows, "", "SETUP " + f_vTime(play.setupTime) + " · ACTIVE " + f_vTime(play.activationTime), tip, 3)
f_vAdd(rows, "", "OPEN " + f_camPrice(camOpen) + " · " + f_vTime(camOpeningSourceTime) + " UTC", vSourceTip, 3)
f_vAdd(rows, "", f_vWrap("SOURCE " + camSourceLabel + " · close " + camCloseLabel + " · " + camDecisionLabel, 40, 2), vSourceTip, 3)
f_vAdd(rows, "", "ORIGINAL EXECUTION\n" + stopModel + " SL / " + targetModel + " TP · min " + str.tostring(minRewardR) + "R", vRiskTip, 3)
f_vAdd(rows, "", CAM_IS_LAB ? "ALERT · alert() calls = reference JSON\nOrder fills = broker-emulated orders" : "ALERT · activation condition + bar close\nJSON · Any alert() function call", "Choose the desired condition; recreate after input/code changes. Reference plans are not fills. No alert delivery is proven by this display.", 3)
else
f_vAdd(rows, "", "INSPECT · SESSION GUIDE", "Choose a code to audit its prerequisites. Chart tracking remains independent.", 4)
f_vAdd(rows, "", f_vWrap(f_vRecovery(), 38, 3), vSourceTip, 0)
f_vAdd(rows, "", vTracking ? f_vWrap("CHART TRACKS · " + f_vDirection(vTracked.dir) + " " + f_vSnapshotName(vTracked), 38, 2) : "CHART · no active plan", "Manual inspection cannot change tracked snapshot selection.", 3)
f_vInsetEnd(rows, vFocusAnchor)
string signature = f_vSignature(rows)
if na(vFocus) or signature != vFocusSignature
if not na(vFocus)
table.delete(vFocus)
vFocus := table.new(f_vPosition(vFocusAnchor), 2, array.size(rows), bgcolor = color.new(vBg, 100), frame_width = 0, border_width = 0)
vFocusSignature := signature
f_vRender(vFocus, rows, vFocusAnchor)
Optional broker emulator: independent fills, occupancy, sizing and costs; no claimed edge.
v2.1.0 · MIT · SHA256 16d881cad4a2acb7ab67a286c1b9b5e19e9d678f974afe1cb72c014a1f694e5f
//@version=6
// CAMARILLA DESK 2.1.0 · MIT · Original interpretation of public Camarilla concepts.
// CARD: market/timeframe — standard time-based intraday bars, 1m through 1h.
// CARD: idea — confirmed play states, frozen prior-source map and reference plans.
// ACTIVE denotes an observed hypothesis, not a filled order or a validated edge.
// See docs/engine-contract.md for exact rules and docs/build-report.md for evidence.
strategy("Camarilla Desk · Strategy Lab", "Cam Lab", overlay = true, behind_chart = false, max_lines_count = 220, max_labels_count = 160, max_boxes_count = 50, max_bars_back = 3000, pyramiding = 0, initial_capital = 100000, currency = currency.USD, commission_type = strategy.commission.percent, commission_value = 0.05, slippage = 1, process_orders_on_close = false, calc_on_every_tick = false, calc_on_order_fills = false, margin_long = 100, margin_short = 100)
const string CAM_VERSION = "2.1.0"
const bool CAM_IS_LAB = true
// PUBLIC LITE BASELINE PREREQUISITES · original closed-bar execution · no Premium parity.
string CAM_FORMULA = "expanded6-v1"
string camSourceGroup = "Source and decision clock"
string sourceMode = input.string("Market default", "Pivot source", options = ["Market default", "Auto RTH/ETH", "RTH", "ETH", "Exchange daily", "Selected session"], group = camSourceGroup, display = display.none, tooltip = "Market default explicitly chooses RTH for non-crypto and Exchange daily for crypto. RTH/ETH require complete observed one-minute windows. Optional Auto RTH/ETH requires complete pre/postmarket coverage and freezes at the opening; it never silently falls back. Exchange daily remains a provider-defined daily/settlement source, never cash RTH.")
string decisionMode = input.string("Auto", "Decision window", options = ["Auto", "US cash", "Exchange day", "Custom"], group = camSourceGroup, display = display.none, tooltip = "Auto uses Exchange day for crypto and US cash 09:30–16:00 New York for other assets. Set Custom for another exchange or a futures cash window.")
string customSession = input.session("0930-1600", "Custom decision interval", group = camSourceGroup, active = decisionMode == "Custom", display = display.none)
string customTimezone = input.string("America/New_York", "Custom IANA time zone", group = camSourceGroup, active = decisionMode == "Custom", display = display.none, tooltip = "Use an IANA region such as America/New_York. True overnight intervals use the ending trading weekday. Equal start/end is 24 hours and uses the starting calendar weekday.")
bool customWeekdays = input.bool(true, "Custom weekdays only", group = camSourceGroup, active = decisionMode == "Custom", display = display.none)
string regularSession = input.session("0930-1600", "RTH source interval", group = camSourceGroup, display = display.none)
string regularTimezone = input.string("America/New_York", "RTH source IANA zone", group = camSourceGroup, display = display.none, tooltip = "Cash-session definition, including futures emulation. It is independent from the exchange's definition of regular trading.")
string extendedClock = input.string("Auto", "ETH source clock", options = ["Auto", "Custom"], group = camSourceGroup, display = display.none, tooltip = "Auto: US equities 04:00–20:00 New York, futures 18:00–17:00 New York, crypto exchange day. Other venues require a Custom source clock.")
string extendedSession = input.session("1800-1700", "Custom ETH interval", group = camSourceGroup, active = extendedClock == "Custom", display = display.none)
string extendedTimezone = input.string("America/New_York", "Custom ETH IANA zone", group = camSourceGroup, active = extendedClock == "Custom", display = display.none)
string closeMode = input.string("Match source", "Pivot close selection", options = ["Match source", "RTH", "ETH", "Exchange daily"], group = camSourceGroup, display = display.none)
float switchingTolerance = input.float(2, "Auto excursion · % selected ATR", minval = 0, maxval = 100, step = 0.1, group = camSourceGroup, active = sourceMode == "Auto RTH/ETH", display = display.none)
string camPrerequisiteGroup = "Public Lite baseline · confirmed prerequisites"
float rangeThresholdPct = input.float(2, "Neutral close change · % selected ATR", minval = 0, maxval = 100, step = 0.1, group = camPrerequisiteGroup, display = display.none)
float widthThresholdPct = input.float(2, "Similar width tolerance · %", minval = 0, maxval = 99, step = 0.1, group = camPrerequisiteGroup, display = display.none)
bool allowNeutral = input.bool(false, "Allow both directions in Neutral", group = camPrerequisiteGroup, display = display.none)
bool smaGate = input.bool(false, "Original SMA close confirmation", group = camPrerequisiteGroup, display = display.none, tooltip = "Optional original filter: confirmed long closes above the chart SMA, short below. This does not reproduce an undisclosed Premium SMA trigger.")
int smaLength = input.int(20, "Chart SMA length", minval = 2, maxval = 300, group = camPrerequisiteGroup, active = smaGate, display = display.none)
string camPlayGroup = "Original structural hypotheses"
bool enableA = input.bool(true, "A · Third-band traverse", group = camPlayGroup, display = display.none)
bool enableB = input.bool(true, "B · Interior breakout and retest", group = camPlayGroup, display = display.none)
bool enableC = input.bool(true, "C · Opening continuation", group = camPlayGroup, display = display.none)
bool enableD = input.bool(true, "D · Context-opposed continuation", group = camPlayGroup, display = display.none)
bool enableE = input.bool(true, "E · Fourth-tier failure", group = camPlayGroup, display = display.none)
bool enableF = input.bool(true, "F · Outer-tier rejection", group = camPlayGroup, display = display.none)
bool enableG = input.bool(false, "G · Opposite-side outer rejection", group = camPlayGroup, display = display.none)
int setupDeadline = input.int(12, "Setup deadline · completed bars", minval = 1, maxval = 200, group = camPlayGroup, display = display.none, tooltip = "Confirmation is allowed through age 12 at the default. The following closed bar expires a pending setup.")
int activeDeadline = input.int(120, "Active deadline · completed bars", minval = 1, maxval = 1000, group = camPlayGroup, display = display.none, tooltip = "Observed later-bar outcomes precede timeout. Default timeout occurs on the 120th later completed chart bar.")
float minRewardR = input.float(1.0, "Minimum structural reward / risk", minval = 0.1, maxval = 10, step = 0.1, group = camPlayGroup, display = display.none)
int stopBufferTicks = input.int(1, "Stop buffer · minimum ticks", minval = 0, maxval = 100, group = camPlayGroup, display = display.none)
string stopModel = input.string("Structural", "Reference stop model", options = ["Structural", "ATR"], group = camPlayGroup, display = display.none, tooltip = "ATR is an original alternative measured from activation close using the frozen selected ATR basis. Neither option claims a vendor preset.")
float stopAtrMultiple = input.float(0.25, "ATR stop multiple", minval = 0.01, maxval = 10, step = 0.05, group = camPlayGroup, active = stopModel == "ATR", display = display.none)
string targetModel = input.string("Structural", "Reference target model", options = ["Structural", "ATR"], group = camPlayGroup, display = display.none)
float targetAtrMultiple = input.float(0.5, "ATR target multiple", minval = 0.01, maxval = 20, step = 0.05, group = camPlayGroup, active = targetModel == "ATR", display = display.none)
float maxTargetAtr = input.float(0, "Maximum target distance · selected ATR (0 off)", minval = 0, maxval = 20, step = 0.1, group = camPlayGroup, display = display.none, tooltip = "Optional original reachability guard; rejects a too-distant target without silently moving it.")
bool allowRearm = input.bool(false, "Allow another episode after ending", group = camPlayGroup, display = display.none)
int cooldownBars = input.int(3, "Rearm cooldown · completed bars", minval = 1, maxval = 200, group = camPlayGroup, active = allowRearm, display = display.none)
int maxEpisodes = input.int(1, "Maximum episodes per code / session", minval = 1, maxval = 10, group = camPlayGroup, active = allowRearm, display = display.none)
string camAlertGroup = "Aggregate JSON alerts"
bool alertActivation = input.bool(true, "Include activation events", group = camAlertGroup, display = display.none)
bool alertEnding = input.bool(true, "Include ending events", group = camAlertGroup, display = display.none)
bool alertArmed = input.bool(false, "Include armed events", group = camAlertGroup, display = display.none)
// Appended to preserve the ordering of all previous core inputs.
string atrBasisMode = input.string("Market default", "ATR calculation basis", options = ["Market default", "Provider daily", "RTH session"], group = camSourceGroup, display = display.none, tooltip = "Market default uses completed observed RTH-session Wilder ATR(14) for futures with a non-Exchange-day decision clock, and completed provider daily ATR(14) otherwise. RTH uses the configured RTH interval and zone; seed = mean of 14 valid session true ranges, then Wilder smoothing. An observed incomplete/malformed session resets the seed. Entirely absent days are skipped: this is observed-session coverage, not an exchange holiday calendar. No fallback. The chosen basis is frozen at the decision opening and drives range, Auto excursion and optional ATR risk/target checks.")
type CamLevels
bool valid = false
bool outerValid = false
bool complete = false
float h = na
float l = na
float c = na
float h2 = na
float l2 = na
float h3 = na
float l3 = na
float h4 = na
float l4 = na
float h5 = na
float l5 = na
float h6 = na
float l6 = na
int sourceStart = na
int sourceEnd = na
string reason = "Source unavailable"
type CamWindowBook
CamLevels latest
CamLevels prior
int start = na
int finish = na
int lastClose = na
float h = na
float l = na
float c = na
int samples = 0
bool complete = false
bool finished = true
string reason = "No observed interval"
// Constant-size Wilder state, updated only when an observed RTH window finishes.
type CamAtrBook
int samples = 0
float seedSum = 0
float previousClose = na
float value = na
int sourceStart = na
int sourceEnd = na
int observedEnd = na
type CamSourcePacket
CamLevels current
CamLevels prior
bool ready = false
bool openKnown = false
float opening = na
int openingTime = na
int sessionStart = na
int sessionEnd = na
int cutoff = na
string clockLabel = ""
string sourceKind = ""
string closeKind = ""
int closeSourceEnd = na
int priorCloseSourceEnd = na
float atr = na
int atrStamp = na
int atrBasisCode = 0
int atrSamples = 0
int atrStart = na
bool atrReady = false
float excursion = na
float excursionThreshold = na
string reason = "Waiting for an observed opening in the 1m source context"
string selectionReason = ""
string coverage = ""
int sourceCode = 0
int closeCode = 0
int reasonCode = 0
int selectionCode = 0
int rthSamples = 0
int ethSamples = 0
int selectedSamples = 0
bool rthComplete = false
bool ethComplete = false
bool selectedComplete = false
type Play
string code = ""
string name = ""
string family = ""
int biasRequired = 0
int dir = 0
string state = "DATA WAIT"
string reason = "Waiting for a decision session"
string next = "Wait for a fully observed opening"
string invalidation = "No setup observed"
bool eligible = false
bool candidate = false
bool approaching = false
bool rangeOk = false
bool widthOk = false
bool openOk = false
bool regionOk = false
bool blocked = false
bool smaOk = true
int episode = 0
float triggerPrice = na
string triggerOperator = ""
string sourceKind = ""
string closeKind = ""
string stopMode = "Structural"
string targetMode = "Structural"
float atrUsed = na
int atrStamp = na
string atrBasis = "Unavailable"
int atrSamples = 0
int atrStart = na
int sourceCutoff = na
int closeSourceEnd = na
float level = na
int setupBar = na
int setupTime = na
float armedHigh = na
float armedLow = na
int activationBar = na
int activationTime = na
int activationSeq = na
float entry = na
float stop = na
float target = na
float effectiveR = na
int sessionStart = na
int sourceStart = na
int sourceEnd = na
int endBar = na
int endTime = na
float outcomePrice = na
bool ambiguous = false
type CamPlanSnapshot
string code = ""
string name = ""
int dir = 0
int episode = 0
int sessionStart = na
int sourceStart = na
int sourceEnd = na
string sourceKind = ""
string closeKind = ""
int setupTime = na
float triggerPrice = na
string triggerOperator = ""
float atrUsed = na
string stopMode = ""
string targetMode = ""
int activationBar = na
int activationTime = na
float entry = na
float stop = na
float target = na
float effectiveR = na
int endBar = na
int endTime = na
float outcomePrice = na
string state = ""
string reason = ""
bool ambiguous = false
string atrBasis = "Unavailable"
int atrSamples = 0
int atrStart = na
int atrStamp = na
type CamEvent
int seq = 0
string code = ""
string kind = ""
string reason = ""
int dir = 0
int eventTime = na
int sessionStart = na
int sourceStart = na
int sourceEnd = na
float price = na
float entry = na
float stop = na
float target = na
bool ambiguous = false
int episode = 0
string sourceKind = ""
string closeKind = ""
int atrStamp = na
int sourceCutoff = na
int closeSourceEnd = na
float atrUsed = na
string atrBasis = "Unavailable"
int atrSamples = 0
int atrStart = na
var array<Play> plays = array.new<Play>()
var array<CamPlanSnapshot> camPlans = array.new<CamPlanSnapshot>()
var array<CamEvent> camEvents = array.new<CamEvent>()
var array<CamEvent> camBarEvents = array.new<CamEvent>()
var array<int> camEventCounter = array.new_int(1, 0)
// Comparison normalization is relative to the actual operand scale, never to 1.
// Self-normalized sign avoids Pine comparison operators' nine-decimal rounding.
f_camSign(float value) =>
na(value) ? 0 : int(nz(value / math.abs(value), 0))
f_camCompare(float first, float rhs) =>
float difference = first - rhs
float tolerance = 4.0 * 2.220446049250313e-16 * math.max(math.abs(first), math.abs(rhs))
na(difference) ? 0 : f_camSign(math.abs(difference) - tolerance) <= 0 ? 0 : f_camSign(difference)
f_camNumber(float value) =>
string result = "null"
if not na(value)
if f_camSign(value) == 0
result := "0"
else
int exponent = int(math.floor(math.log10(math.abs(value))))
result := exponent < -6 or exponent > 12 ? str.tostring(value / math.pow(10, exponent), "0.################") + "e" + str.tostring(exponent) : str.tostring(value, "0.################")
result
f_camPrice(float value) =>
na(value) ? "—" : str.tostring(value, format.mintick)
f_camExact(float value) =>
na(value) ? "—" : f_camNumber(value)
// Narrative rounding suppresses binary float tails; raw map/plan values stay unchanged.
f_camReadable(float value) =>
na(value) ? "—" : f_camCompare(math.abs(value), 0.000001) < 0 ? f_camNumber(value) : str.tostring(value, "0.##########")
f_camTerminal(string state) =>
state == "REJECTED" or state == "INVALIDATED" or state == "TARGET" or state == "STOP" or state == "EXPIRED"
f_camTouched(float level, float barHigh, float barLow) =>
not na(level) and not na(barLow) and not na(barHigh) and f_camCompare(barLow, level) <= 0 and f_camCompare(barHigh, level) >= 0
// A flat positive bar is valid. Missing or incoherent constituent prices are not.
f_camBarValid(float barOpen, float barHigh, float barLow, float barClose) =>
not na(barOpen) and not na(barHigh) and not na(barLow) and not na(barClose) and f_camSign(barLow) > 0 and f_camCompare(barHigh, math.max(barOpen, barClose)) >= 0 and f_camCompare(barLow, math.min(barOpen, barClose)) <= 0
f_camLevels(float srcHigh, float srcLow, float srcClose, int srcStart, int srcEnd, bool complete, string incompleteReason, bool requireCloseInside = true) =>
CamLevels m = CamLevels.new()
m.h := srcHigh
m.l := srcLow
m.c := srcClose
m.sourceStart := srcStart
m.sourceEnd := srcEnd
bool independentValid = not na(srcHigh) and not na(srcLow) and not na(srcClose) and f_camSign(srcLow) > 0 and f_camSign(srcClose) > 0 and f_camCompare(srcHigh, srcLow) >= 0
m.complete := complete and (requireCloseInside ? f_camBarValid(srcClose, srcHigh, srcLow, srcClose) : independentValid)
bool coherent = m.complete and f_camCompare(srcHigh, srcLow) > 0
if complete and coherent
float width = srcHigh - srcLow
m.h2 := srcClose + 1.1 * width / 6
m.l2 := srcClose - 1.1 * width / 6
m.h3 := srcClose + 1.1 * width / 4
m.l3 := srcClose - 1.1 * width / 4
m.h4 := srcClose + 1.1 * width / 2
m.l4 := srcClose - 1.1 * width / 2
m.h5 := m.h4 + 1.168 * (m.h4 - m.h3)
m.l5 := m.l4 - 1.168 * (m.l3 - m.l4)
m.h6 := srcHigh / srcLow * srcClose
m.l6 := 2 * srcClose - m.h6
m.valid := f_camCompare(m.h4, m.h3) > 0 and f_camCompare(m.h3, m.l3) > 0 and f_camCompare(m.l3, m.l4) > 0
m.outerValid := m.valid and not na(m.h6) and not na(m.l6) and f_camSign(m.l6) > 0 and f_camCompare(m.h6, m.h4) > 0 and f_camCompare(m.l6, m.l4) < 0
m.reason := not m.valid ? "Collapsed inner geometry" : not m.outerValid ? "Outer ratio geometry unavailable" : "Complete coherent source"
else
m.reason := not complete ? incompleteReason : "Missing, nonpositive or incoherent source H/L/C"
m
f_camRelationship(CamLevels current, CamLevels previous) =>
string relation = "Unavailable"
int bias = 0
float ratio = na
string widthLabel = "Unavailable"
if current.valid and previous.valid
float a = previous.l3
float b = previous.h3
float c = current.l3
float d = current.h3
int lowerCompare = f_camCompare(c, a)
int upperCompare = f_camCompare(d, b)
bias := lowerCompare > 0 and upperCompare > 0 ? 1 : lowerCompare < 0 and upperCompare < 0 ? -1 : 0
relation := lowerCompare == 0 and upperCompare == 0 ? "Unchanged" : f_camCompare(c, b) > 0 ? "Higher separated" : f_camCompare(d, a) < 0 ? "Lower separated" : lowerCompare >= 0 and upperCompare <= 0 ? "Inside band" : lowerCompare <= 0 and upperCompare >= 0 ? "Outside band" : lowerCompare > 0 and upperCompare > 0 ? "Overlapping higher" : "Overlapping lower"
ratio := (d - c) / (b - a)
int widthCompare = f_camCompare(d + a, c + b)
widthLabel := widthCompare > 0 ? "Wide" : widthCompare < 0 ? "Narrow" : "Equal"
[relation, bias, ratio, widthLabel]
// Independent transcription of public Lite 2.0 prerequisites, applied on closed bars.
// Original episode triggers below are a separate layer; these flags are not entries.
f_camPrerequisites(string code, CamLevels m, CamLevels prior, float atr, float opening, float barOpen, float barClose, bool priorBlocked, float rangePct, float widthPct, bool neutralAllowed) =>
string family = str.substring(code, 1, 2)
bool higher = str.substring(code, 0, 1) == "H"
bool innerReady = m.valid and prior.valid
bool atrReady = not na(atr) and f_camSign(atr) > 0
bool observationReady = not na(opening) and not na(barOpen) and not na(barClose)
float threshold = atr * rangePct / 100
float delta = m.c - prior.c
int bias = innerReady and atrReady ? (f_camCompare(m.c, prior.c + threshold) > 0 ? 1 : f_camCompare(m.c, prior.c - threshold) < 0 ? -1 : 0) : 0
float width = m.h3 - m.l3
float previousWidth = prior.h3 - prior.l3
float ratio = innerReady and f_camSign(previousWidth) > 0 ? width / previousWidth : na
// Cross-sums retain the original endpoint scale when subtracting nearby prices.
float upperFactor = 1 + widthPct / 100
float lowerFactor = 1 - widthPct / 100
string widthLabel = na(ratio) ? "Unavailable" : f_camCompare(m.h3 + prior.l3 * upperFactor, m.l3 + prior.h3 * upperFactor) > 0 ? "Wide" : f_camCompare(m.h3 + prior.l3 * lowerFactor, m.l3 + prior.h3 * lowerFactor) < 0 ? "Narrow" : "Similar"
bool rangeOk = innerReady and atrReady and (bias == (higher ? 1 : -1) or bias == 0 and neutralAllowed)
bool widthOk = not na(ratio) and (family == "F" or family == "G" or widthLabel == "Similar" or (family == "A" or family == "E" ? widthLabel == "Wide" : widthLabel == "Narrow"))
float openLow = family == "A" ? m.l2 : family == "B" or family == "E" ? (higher ? m.l2 : m.l3) : family == "G" ? (higher ? m.l6 : m.c) : family == "D" ? (higher ? m.l6 : m.h4) : (higher ? m.h4 : m.l6)
float openHigh = family == "A" ? m.h2 : family == "B" or family == "E" ? (higher ? m.h3 : m.h2) : family == "G" ? (higher ? m.c : m.h6) : family == "D" ? (higher ? m.l4 : m.h6) : (higher ? m.h6 : m.l4)
bool openOk = observationReady and f_camCompare(opening, openLow) >= 0 and f_camCompare(opening, openHigh) <= 0
bool upperSide = higher != (family == "D" or family == "G")
bool regionOk = observationReady and (family == "A" ? (higher ? f_camCompare(barClose, m.l4) > 0 and f_camCompare(barClose, m.c) < 0 : f_camCompare(barClose, m.h4) < 0 and f_camCompare(barClose, m.c) > 0) : family == "B" or family == "E" ? (higher ? f_camCompare(barClose, m.h3) > 0 and f_camCompare(barClose, m.h6) < 0 : f_camCompare(barClose, m.l3) < 0 and f_camCompare(barClose, m.l6) > 0) : family == "C" or family == "D" ? (upperSide ? f_camCompare(barClose, m.h3) > 0 : f_camCompare(barClose, m.l3) < 0) : (upperSide ? f_camCompare(barClose, m.h4) > 0 : f_camCompare(barClose, m.l4) < 0))
float badLow = family == "A" ? (higher ? m.l4 : m.l2) : family == "B" or family == "E" ? (higher ? m.l2 : m.l6) : upperSide ? (family == "G" ? m.c : m.h3) : m.l6 - (m.l4 - m.l6) * 0.5
float badHigh = family == "A" ? (higher ? m.h2 : m.h4) : family == "B" or family == "E" ? (higher ? m.h6 : m.h2) : upperSide ? m.h6 + (m.h6 - m.h4) * 0.5 : (family == "G" ? m.c : m.l3)
bool blocked = priorBlocked or (observationReady and (f_camCompare(barOpen, badLow) < 0 or f_camCompare(barOpen, badHigh) > 0))
bool geometryReady = innerReady and (family == "A" or m.outerValid)
bool candidate = geometryReady and atrReady and observationReady and rangeOk and widthOk and openOk and not blocked
bool approaching = candidate and regionOk
[bias, ratio, widthLabel, rangeOk, widthOk, openOk, regionOk, blocked, candidate, approaching]
f_camCodeIndex(string code) =>
int found = -1
if array.size(plays) > 0
for i = 0 to array.size(plays) - 1
if array.get(plays, i).code == code
found := i
found
f_camAutoFocus() =>
int found = -1
int latest = na
if array.size(plays) > 0
for i = 0 to array.size(plays) - 1
Play p = array.get(plays, i)
if p.state == "ACTIVE" and (na(latest) or p.activationTime > latest)
latest := p.activationTime
found := i
found
f_camStorePlan(Play p) =>
if not na(p.activationTime)
CamPlanSnapshot snapshot = CamPlanSnapshot.new(p.code, p.name, p.dir, p.episode, p.sessionStart, p.sourceStart, p.sourceEnd, p.sourceKind, p.closeKind, p.setupTime, p.triggerPrice, p.triggerOperator, p.atrUsed, p.stopMode, p.targetMode, p.activationBar, p.activationTime, p.entry, p.stop, p.target, p.effectiveR, p.endBar, p.endTime, p.outcomePrice, p.state, p.reason, p.ambiguous)
snapshot.atrBasis := p.atrBasis
snapshot.atrSamples := p.atrSamples
snapshot.atrStart := p.atrStart
snapshot.atrStamp := p.atrStamp
int found = -1
if array.size(camPlans) > 0
for i = 0 to array.size(camPlans) - 1
CamPlanSnapshot old = array.get(camPlans, i)
if old.code == p.code and old.sessionStart == p.sessionStart and old.episode == p.episode
found := i
break
if found >= 0
array.remove(camPlans, found)
array.unshift(camPlans, snapshot)
if array.size(camPlans) > 40
// Never evict an ongoing plan merely because other codes re-entered.
int oldestEnded = -1
for i = array.size(camPlans) - 1 to 0
if array.get(camPlans, i).state != "ACTIVE"
oldestEnded := i
break
if oldestEnded >= 0
array.remove(camPlans, oldestEnded)
p
f_camEmit(Play p, string kind, string reason, float price, int stamp, bool ambiguous) =>
int seq = array.get(camEventCounter, 0) + 1
array.set(camEventCounter, 0, seq)
CamEvent e = CamEvent.new(seq, p.code, kind, reason, p.dir, stamp, p.sessionStart, p.sourceStart, p.sourceEnd, price, p.entry, p.stop, p.target, ambiguous)
e.episode := p.episode
e.sourceKind := p.sourceKind
e.closeKind := p.closeKind
e.atrStamp := p.atrStamp
e.atrUsed := p.atrUsed
e.atrBasis := p.atrBasis
e.atrSamples := p.atrSamples
e.atrStart := p.atrStart
e.sourceCutoff := p.sourceCutoff
e.closeSourceEnd := p.closeSourceEnd
f_camStorePlan(p)
array.unshift(camEvents, e)
array.push(camBarEvents, e)
if array.size(camEvents) > 60
array.pop(camEvents)
seq
f_camArm(Play p, float barHigh, float barLow, int bar, int stamp, string reason) =>
p.episode += 1
p.state := "ARMED"
p.setupBar := bar
p.setupTime := stamp
p.armedHigh := barHigh
p.armedLow := barLow
p.reason := reason
f_camEmit(p, "ARMED", reason, p.level, stamp, false)
p
f_camEnd(Play p, string kind, string reason, float price, int bar, int stamp, bool ambiguous) =>
p.state := kind
p.reason := reason
p.next := "Episode ended · next dated session"
p.endBar := bar
p.endTime := stamp
p.outcomePrice := price
p.ambiguous := ambiguous
f_camEmit(p, kind, reason, price, stamp, ambiguous)
p
// Normalize only machine-noise near a whole tick; retain actual sub-tick prices.
f_camDistance(float first, float rhs, float tick) =>
float raw = math.abs(first - rhs)
float ticks = raw / tick
float nearest = math.round(ticks)
float noise = 8.0 * 2.220446049250313e-16 * math.max(math.max(math.abs(first), math.abs(rhs)), tick) / tick
f_camSign(math.abs(ticks - nearest) - math.min(noise, 0.000001)) <= 0 ? nearest * tick : raw
f_camGrid(float value, float tick, bool upward) =>
float ticks = value / tick
float nearest = math.round(ticks)
float tolerance = math.min(8.0 * 2.220446049250313e-16 * math.max(math.abs(ticks), 1), 0.000001)
float stable = f_camSign(math.abs(ticks - nearest) - tolerance) <= 0 ? nearest : ticks
(upward ? math.ceil(stable) : math.floor(stable)) * tick
f_camPlan(int dir, string family, CamLevels m, float armedHigh, float armedLow, float entry, bool wide, float tick, int bufferTicks, float minimumR) =>
float boundary = na
float destination = na
if family == "A"
boundary := dir == 1 ? math.min(m.l4, armedLow) : math.max(m.h4, armedHigh)
destination := dir == 1 ? m.h3 : m.l3
else if family == "B" or family == "C" or family == "D"
boundary := dir == 1 ? m.h3 : m.l3
destination := dir == 1 ? m.h6 : m.l6
else if family == "E"
boundary := dir == 1 ? math.min(m.l6, armedLow) : math.max(m.h6, armedHigh)
destination := dir == 1 ? m.h4 : m.l4
else
boundary := dir == 1 ? math.min(armedLow, m.l6 - 0.25 * (m.l3 - m.l4)) : math.max(armedHigh, m.h6 + 0.25 * (m.h4 - m.h3))
destination := wide ? (dir == 1 ? m.l4 : m.h4) : m.c
float stopRaw = boundary - dir * bufferTicks * tick
float stop = f_camGrid(stopRaw, tick, dir == -1)
float target = f_camGrid(destination, tick, dir == -1)
bool positive = not na(entry) and not na(stop) and not na(target) and f_camSign(entry) > 0 and f_camSign(stop) > 0 and f_camSign(target) > 0
bool directionOk = positive and f_camCompare(entry, stop) * dir > 0 and f_camCompare(target, entry) * dir > 0
float risk = directionOk ? f_camDistance(entry, stop, tick) : na
float reward = directionOk ? f_camDistance(entry, target, tick) : na
float effectiveR = directionOk and f_camSign(risk) > 0 ? reward / risk : na
bool valid = directionOk and f_camCompare(risk / tick, 1) >= 0 and f_camCompare(effectiveR, minimumR) >= 0
string reason = not positive ? "Nonpositive or unavailable plan prices" : not directionOk ? "Destination or stop is on the wrong side of entry" : f_camCompare(risk / tick, 1) < 0 ? "Risk distance is smaller than one tick" : f_camCompare(effectiveR, minimumR) < 0 ? "Structural R " + f_camExact(effectiveR) + " < minimum " + f_camExact(minimumR) : "Confirmed structural hypothesis · reference plan only"
[valid, stop, target, effectiveR, reason]
f_camPlanOptions(int dir, string family, CamLevels m, float armedHigh, float armedLow, float entry, bool wide, float tick, int bufferTicks, float minimumR, float atr, string stopChoice, float stopMultiple, string targetChoice, float targetMultiple, float reachLimit) =>
[structuralOk, structuralStop, structuralTarget, structuralR, structuralReason] = f_camPlan(dir, family, m, armedHigh, armedLow, entry, wide, tick, bufferTicks, minimumR)
bool atrReady = not na(atr) and f_camSign(atr) > 0
float stop = stopChoice == "ATR" and atrReady ? f_camGrid(entry - dir * (atr * stopMultiple + bufferTicks * tick), tick, dir == -1) : structuralStop
float target = targetChoice == "ATR" and atrReady ? f_camGrid(entry + dir * atr * targetMultiple, tick, dir == -1) : structuralTarget
bool positive = not na(stop) and not na(target) and f_camSign(entry) > 0 and f_camSign(stop) > 0 and f_camSign(target) > 0
bool correctSide = positive and f_camCompare(entry, stop) * dir > 0 and f_camCompare(target, entry) * dir > 0
float risk = correctSide ? f_camDistance(entry, stop, tick) : na
float reward = correctSide ? f_camDistance(entry, target, tick) : na
float rr = correctSide and f_camSign(risk) > 0 ? reward / risk : na
bool enoughRisk = correctSide and f_camCompare(risk / tick, 1) >= 0
bool enoughReward = enoughRisk and f_camCompare(rr, minimumR) >= 0
bool reachable = reachLimit == 0 or atrReady and f_camCompare(reward, atr * reachLimit) <= 0
bool valid = atrReady and enoughReward and reachable
string reason = not atrReady ? "Confirmed selected ATR unavailable" : not positive ? "Nonpositive or unavailable reference prices" : not correctSide ? "Stop or target is on the wrong side" : not enoughRisk ? "Risk is smaller than one tick" : not enoughReward ? "Reference R " + f_camExact(rr) + " < minimum " + f_camExact(minimumR) : not reachable ? "Target exceeds " + f_camExact(reachLimit) + " selected ATR reach limit" : "Original confirmed reference geometry"
[valid, stop, target, rr, reason]
f_camOutcome(Play p, float barOpen, float barHigh, float barLow, int bar, int stamp, bool contained, int timeout, bool sessionEnded) =>
string kind = ""
string reason = ""
float price = na
bool ambiguous = false
if p.state == "ACTIVE" and bar > p.activationBar
if contained and f_camBarValid(barOpen, barHigh, barLow, barOpen)
bool stopGap = f_camCompare(barOpen, p.stop) * p.dir <= 0
bool targetGap = f_camCompare(barOpen, p.target) * p.dir >= 0
bool stopTouch = p.dir == 1 ? f_camCompare(barLow, p.stop) <= 0 : f_camCompare(barHigh, p.stop) >= 0
bool targetTouch = p.dir == 1 ? f_camCompare(barHigh, p.target) >= 0 : f_camCompare(barLow, p.target) <= 0
if stopGap
kind := "STOP"
price := barOpen
reason := "Opening price beyond stop · reference outcome at observed open"
else if targetGap
kind := "TARGET"
price := p.target
reason := "Opening price beyond destination · fixed reference destination"
else if stopTouch
kind := "STOP"
price := p.stop
ambiguous := targetTouch
reason := targetTouch ? "Both levels touched · stop-first convention; path unknown" : "Reference stop touched on a later bar"
else if targetTouch
kind := "TARGET"
price := p.target
reason := "Reference destination touched on a later bar"
if kind == "" and bar - p.activationBar >= timeout
kind := "EXPIRED"
reason := "Completed-bar active deadline · no fill inferred"
if kind == "" and sessionEnded
kind := "EXPIRED"
reason := contained ? "Decision session ended · no fill inferred" : "Session end observed later or across a boundary · no fill inferred"
[kind, price, ambiguous, reason]
f_camFamilyEnabled(int familyIndex) =>
familyIndex == 0 ? enableA : familyIndex == 1 ? enableB : familyIndex == 2 ? enableC : familyIndex == 3 ? enableD : familyIndex == 4 ? enableE : familyIndex == 5 ? enableF : enableG
f_camNewPlay(int index, CamLevels m, bool mapReady, string dataReason, int bias, float opening, bool openingKnown, int sessionStart) =>
int familyIndex = int(math.floor(index / 2))
bool higher = index % 2 == 0
string family = familyIndex == 0 ? "A" : familyIndex == 1 ? "B" : familyIndex == 2 ? "C" : familyIndex == 3 ? "D" : familyIndex == 4 ? "E" : familyIndex == 5 ? "F" : "G"
string familyName = family == "A" ? "Traverse" : family == "B" ? "Break and retest" : family == "C" ? "Opening continuation" : family == "D" ? "Opposed continuation" : family == "E" ? "Fourth-tier failure" : family == "F" ? "Outer rejection" : "Opposite outer rejection"
Play p = Play.new()
p.code := (higher ? "H" : "L") + family
p.name := (higher ? "Higher · " : "Lower · ") + familyName
p.family := family
p.biasRequired := higher ? 1 : -1
p.dir := (family == "D" or family == "E" or family == "F") ? (higher ? -1 : 1) : (higher ? 1 : -1)
p.sessionStart := sessionStart
p.sourceStart := m.sourceStart
p.sourceEnd := m.sourceEnd
bool longSide = p.dir == 1
p.level := family == "A" ? (longSide ? m.l3 : m.h3) : family == "E" ? (longSide ? m.l4 : m.h4) : family == "F" or family == "G" ? (longSide ? m.l6 : m.h6) : (longSide ? m.h4 : m.l4)
p.triggerPrice := p.level
p.triggerOperator := longSide ? ">" : "<"
string levelText = f_camExact(p.level)
p.next := family == "B" ? "Closed break of " + levelText + "; later contained retest" : family == "E" ? "Closed excursion beyond " + levelText + "; later failure close" : "Touch " + levelText + "; close " + (longSide ? "above" : "below")
p.invalidation := family == "A" ? "Pending close " + (longSide ? "≤ " + f_camExact(m.l4) : "≥ " + f_camExact(m.h4)) : family == "B" or family == "C" or family == "D" ? "Pending close " + (longSide ? "≤ " + f_camExact(m.h3) : "≥ " + f_camExact(m.l3)) : family == "E" ? "Pending close " + (longSide ? "≤ " + f_camExact(m.l6) : "≥ " + f_camExact(m.h6)) : "Pending setup deadline; no invented farther pivot"
if not f_camFamilyEnabled(familyIndex)
p.state := "DISABLED"
p.reason := "Family " + family + " is disabled"
p.next := "Enable this family in settings"
else if not mapReady
p.state := "DATA WAIT"
p.reason := dataReason
else if not openingKnown
p.state := "DATA WAIT"
p.reason := "OPEN UNKNOWN · exact contained session opening not observed"
else if family != "A" and not m.outerValid
p.state := "DATA WAIT"
p.reason := "Outer geometry required by this family's target, stop or cancellation"
else
p.state := "WATCH"
p.reason := "Waiting for confirmed public-baseline prerequisite evaluation"
p
f_camRefreshPlay(Play p, CamLevels m, CamLevels prior, float atr, float opening, float barOpen, float barClose, bool observationValid, bool inWindow, bool sourceReady, string dataReason, bool smaReady) =>
[bias, ratio, width, rangeOk, widthOk, openOk, regionOk, blocked, candidate, approaching] = f_camPrerequisites(p.code, m, prior, atr, opening, barOpen, barClose, p.blocked, rangeThresholdPct, widthThresholdPct, allowNeutral)
p.rangeOk := rangeOk
p.widthOk := widthOk
p.openOk := openOk
p.regionOk := observationValid and regionOk
p.blocked := observationValid and inWindow ? blocked : p.blocked
p.candidate := sourceReady and observationValid and inWindow and candidate and p.state != "DISABLED"
p.approaching := p.candidate and p.regionOk
p.eligible := p.candidate
p.smaOk := smaReady
if p.state == "WATCH" or p.state == "DATA WAIT"
bool prerequisitesAvailable = sourceReady and not na(opening) and not na(atr) and f_camSign(atr) > 0 and (p.family == "A" or m.outerValid)
p.state := prerequisitesAvailable ? "WATCH" : "DATA WAIT"
string widthNeed = p.family == "A" or p.family == "E" ? "Wide or Similar" : p.family == "F" or p.family == "G" ? "any width" : "Narrow or Similar"
p.reason := not sourceReady ? dataReason : na(opening) ? "Exact source-minute opening unavailable" : not prerequisitesAvailable ? "Required geometry or confirmed ATR unavailable" : not inWindow ? "Outside decision window" : not observationValid ? "Malformed chart OHLC · price evaluation withheld" : p.blocked ? "Bad OPEN crossed this family's limit · blocked for this session" : not rangeOk ? "Close-delta context is " + (bias == 1 ? "Higher" : bias == -1 ? "Lower" : "Neutral") + "; this code requires " + (p.biasRequired == 1 ? "Higher" : "Lower") : not widthOk ? "Width " + width + "; requires " + widthNeed : not openOk ? "Frozen open " + f_camExact(opening) + " is outside this family's bounded inclusive opening interval" : not regionOk ? "Candidate eligible · closed price is outside its approach region" : "Approaching · prerequisites pass; original confirmed trigger still required"
p.next := p.blocked or not rangeOk or not widthOk or not openOk ? "Next source session / changed prerequisites" : not inWindow ? "Next decision window" : p.approaching ? "Original close " + p.triggerOperator + " " + f_camExact(p.triggerPrice) + (smaGate and not p.smaOk ? " plus SMA confirmation" : " with the stated setup") : "Enter the play's approach region on a confirmed close"
p
f_camRearm(Play p, int bar, bool inWindow, bool enabled, int cooldown, int episodeLimit) =>
if enabled and inWindow and f_camTerminal(p.state) and not na(p.endBar) and bar > p.endBar and bar - p.endBar >= cooldown and p.episode < episodeLimit and p.candidate and not p.blocked
p.state := "WATCH"
p.setupBar := na
p.setupTime := na
p.armedHigh := na
p.armedLow := na
p.activationBar := na
p.activationTime := na
p.activationSeq := na
p.entry := na
p.stop := na
p.target := na
p.effectiveR := na
p.endBar := na
p.endTime := na
p.outcomePrice := na
p.ambiguous := false
p.reason := "Cooldown completed · waiting for a new original setup"
p
// Explanations report the observed family trigger without changing its lifecycle.
f_camActivationReason(Play p, float activationClose) =>
bool longSide = p.dir == 1
string tier = p.family == "A" ? (longSide ? "L3" : "H3") : p.family == "E" ? (longSide ? "L4" : "H4") : p.family == "F" or p.family == "G" ? (longSide ? "L6" : "H6") : (longSide ? "H4" : "L4")
string cause = p.family == "A" ? tier + " touch + " + (longSide ? "reclaim" : "rejection") : p.family == "B" ? tier + " closed break, later retest" : p.family == "C" or p.family == "D" ? "Bounded opening region; " + tier + " retest held" : p.family == "E" ? "Closed " + (longSide ? "below " : "above ") + tier + ", later failure" : tier + " outer touch + recovery"
cause + "; close " + f_camReadable(activationClose) + (longSide ? " > " : " < ") + tier + " " + f_camReadable(p.level) + (p.family == "D" ? " · against frozen structure" : "")
// Parameterized OHLC/clock step for production and exact-function fixture probes.
// Input p is a single episode row. Only its reference fields and event arrays mutate.
f_camStep(Play p, CamLevels m, float barOpen, float barHigh, float barLow, float barClose, float previousClose, int bar, int stamp, bool contained, bool sessionEnded, bool wide, float tick, int setupLimit, int activeLimit, int bufferTicks, float minimumR) =>
bool usable = contained and f_camBarValid(barOpen, barHigh, barLow, barClose)
if p.state == "ACTIVE"
[kind, price, ambiguous, outcomeReason] = f_camOutcome(p, barOpen, barHigh, barLow, bar, stamp, usable, activeLimit, sessionEnded)
if kind != ""
f_camEnd(p, kind, outcomeReason, price, bar, stamp, ambiguous)
else if p.state == "WATCH" or p.state == "ARMED"
bool longSide = p.dir == 1
bool touched = usable and f_camTouched(p.level, barHigh, barLow)
if usable and p.state == "WATCH" and p.approaching
bool shouldArm = p.family == "B" ? not na(previousClose) and f_camCompare(previousClose, p.level) * p.dir <= 0 and f_camCompare(barClose, p.level) * p.dir > 0 : p.family == "E" ? f_camCompare(barClose, p.level) * p.dir < 0 : p.family == "C" or p.family == "D" ? true : touched
if shouldArm
string armReason = p.family == "B" ? "Confirmed breakout observed · await a later retest" : p.family == "E" ? "Confirmed excursion observed · await a later failure" : p.family == "C" or p.family == "D" ? "Known opening in bounded fourth-to-sixth interval · wait for contained retest" : "Actual bar range touched the reference level"
f_camArm(p, barHigh, barLow, bar, stamp, armReason)
p.next := p.family == "E" ? "Later close " + (longSide ? "above " : "below ") + f_camExact(p.level) : "Close " + (longSide ? "above " : "below ") + f_camExact(p.level) + (p.family == "B" ? " after a later retest" : " after the observed test")
if p.state == "ARMED"
int age = bar - p.setupBar
if age > setupLimit
f_camEnd(p, "EXPIRED", "Setup deadline passed · confirmation allowed only through age " + str.tostring(setupLimit), na, bar, stamp, false)
else if usable
p.armedHigh := math.max(p.armedHigh, barHigh)
p.armedLow := math.min(p.armedLow, barLow)
bool cancelled = p.blocked or (p.family == "A" ? (longSide ? f_camCompare(barClose, m.l4) <= 0 : f_camCompare(barClose, m.h4) >= 0) : p.family == "B" or p.family == "C" or p.family == "D" ? (longSide ? f_camCompare(barClose, m.h3) <= 0 : f_camCompare(barClose, m.l3) >= 0) : p.family == "E" ? (longSide ? f_camCompare(barClose, m.l6) <= 0 : f_camCompare(barClose, m.h6) >= 0) : false)
bool recoveryClose = f_camCompare(barClose, p.level) * p.dir > 0
bool previousWrongSide = not na(previousClose) and f_camCompare(previousClose, p.level) * p.dir <= 0
bool confirmed = p.family == "B" ? bar > p.setupBar and touched and recoveryClose : p.family == "E" ? bar > p.setupBar and recoveryClose : p.family == "C" or p.family == "D" ? touched and recoveryClose : recoveryClose and ((bar == p.setupBar and touched) or previousWrongSide)
if cancelled
f_camEnd(p, "INVALIDATED", p.blocked ? "Bad OPEN crossed the public-baseline limit · session block latched" : "Pending close crossed the original cancellation boundary", barClose, bar, stamp, false)
else if sessionEnded
f_camEnd(p, "EXPIRED", "Decision session ended before activation · no entry inferred", na, bar, stamp, false)
else if confirmed and p.approaching and p.smaOk
[planOk, stop, target, rewardR, planReason] = f_camPlanOptions(p.dir, p.family, m, p.armedHigh, p.armedLow, barClose, wide, tick, bufferTicks, minimumR, p.atrUsed, p.stopMode, stopAtrMultiple, p.targetMode, targetAtrMultiple, maxTargetAtr)
if planOk
p.state := "ACTIVE"
p.entry := barClose
p.stop := stop
p.target := target
p.effectiveR := rewardR
p.activationBar := bar
p.activationTime := stamp
p.reason := f_camActivationReason(p, barClose)
p.next := "Observe later bars: stop " + f_camExact(stop) + " / destination " + f_camExact(target)
p.invalidation := "Fixed reference stop " + f_camExact(stop) + "; session end or active deadline"
p.activationSeq := f_camEmit(p, "ACTIVE", p.reason, barClose, stamp, false)
else
f_camEnd(p, "REJECTED", planReason + " · episode consumed", barClose, bar, stamp, false)
else if sessionEnded
f_camEnd(p, "EXPIRED", "Session end observed across an unavailable boundary · no fill inferred", na, bar, stamp, false)
p
f_camClock(int barTime, int dailyStart, int dailyEnd, string mode, string sessionText, string timezone, bool weekdaysOnly) =>
int startStamp = dailyStart
int endStamp = dailyEnd
bool dayAllowed = true
string label = "Exchange day · provider boundaries"
if mode != "Exchange day"
string interval = mode == "US cash" ? "0930-1600" : sessionText
string zone = mode == "US cash" ? "America/New_York" : timezone
int startHour = int(str.tonumber(str.substring(interval, 0, 2)))
int startMinute = int(str.tonumber(str.substring(interval, 2, 4)))
int endHour = int(str.tonumber(str.substring(interval, 5, 7)))
int endMinute = int(str.tonumber(str.substring(interval, 7, 9)))
int firstMinute = startHour * 60 + startMinute
int lastMinute = endHour * 60 + endMinute
int nowMinute = hour(barTime, zone) * 60 + minute(barTime, zone)
bool overnight = lastMinute <= firstMinute
int dayOffset = overnight and nowMinute < firstMinute ? -1 : 0
int localYear = year(barTime, zone)
int localMonth = month(barTime, zone)
int localDay = dayofmonth(barTime, zone) + dayOffset
startStamp := timestamp(zone, localYear, localMonth, localDay, startHour, startMinute)
endStamp := timestamp(zone, localYear, localMonth, localDay + (overnight ? 1 : 0), endHour, endMinute)
int tradingWeekday = dayofweek(lastMinute < firstMinute ? endStamp : startStamp, zone)
dayAllowed := (mode == "Custom" and not weekdaysOnly) or (tradingWeekday >= dayofweek.monday and tradingWeekday <= dayofweek.friday)
label := (mode == "US cash" ? "US cash · " : "Custom · ") + interval + " " + zone
[startStamp, endStamp, dayAllowed, label]
f_camOpenLocation(float opening, CamLevels m) =>
na(opening) ? "OPEN UNKNOWN" : not m.valid ? "MAP UNAVAILABLE" : f_camCompare(opening, m.l4) < 0 ? "Below L4" : f_camCompare(opening, m.l4) == 0 ? "At L4" : f_camCompare(opening, m.l3) < 0 ? "L4–L3" : f_camCompare(opening, m.l3) == 0 ? "At L3" : f_camCompare(opening, m.h3) < 0 ? "Inside third band" : f_camCompare(opening, m.h3) == 0 ? "At H3" : f_camCompare(opening, m.h4) < 0 ? "H3–H4" : f_camCompare(opening, m.h4) == 0 ? "At H4" : "Above H4"
//
// SOURCE ADAPTER · one-minute observations, session-frozen scalar packet.
// Lower-timeframe request returns an already frozen context, never one candle's
// OHLC as a proxy for an entire source window. Availability is checked again below.
f_camBookFinish(CamWindowBook book, bool complete, string reason) =>
book.prior := book.latest
book.latest := f_camLevels(book.h, book.l, book.c, book.start, book.finish, complete, reason)
book.finished := true
book.complete := complete
book.reason := reason
book
f_camObserve(CamWindowBook book, int start, int finish, bool allowed, int stamp, int closingStamp, float o, float h, float l, float c) =>
bool overlap = allowed and not na(start) and not na(finish) and stamp < finish and closingStamp > start
bool contained = overlap and stamp >= start and closingStamp <= finish
bool valid = f_camBarValid(o, h, l, c)
if not book.finished and (stamp >= book.finish or overlap and start != book.start)
f_camBookFinish(book, false, "Exact final source boundary missing")
if overlap
if na(book.start) or book.start != start
book.start := start
book.finish := finish
book.lastClose := na
book.h := na
book.l := na
book.c := na
book.samples := 0
book.complete := contained and valid and stamp == start
book.finished := false
book.reason := book.complete ? "Collecting contiguous 1m source" : "Exact valid source opening missing"
if not book.finished
if not contained or not valid or not na(book.lastClose) and stamp != book.lastClose
book.complete := false
book.reason := not contained ? "Source minute straddles boundary" : not valid ? "Malformed source OHLC" : "Missing interior source minute"
if contained
if valid
book.h := na(book.h) ? h : math.max(book.h, h)
book.l := na(book.l) ? l : math.min(book.l, l)
book.c := c
book.samples += 1
book.lastClose := closingStamp
if closingStamp >= finish
bool complete = book.complete and contained and closingStamp == finish
f_camBookFinish(book, complete, complete ? "Complete observed 1m interval" : book.reason)
book
// Original observed-session ATR convention, not an undisclosed vendor formula.
// Missing calendar days are not observations; invalid observed windows reset.
// Empty/duplicate/out-of-order packets cannot count twice or rewind the chain.
f_camAtrConsume(CamAtrBook book, CamLevels source) =>
if not na(source.sourceEnd) and (na(book.observedEnd) or source.sourceEnd > book.observedEnd)
book.observedEnd := source.sourceEnd
bool valid = source.complete and not na(source.sourceStart) and source.sourceEnd > source.sourceStart and (na(book.sourceEnd) or source.sourceStart >= book.sourceEnd) and f_camBarValid(source.c, source.h, source.l, source.c)
if valid
float tr = na(book.previousClose) ? source.h - source.l : math.max(source.h - source.l, math.max(math.abs(source.h - book.previousClose), math.abs(source.l - book.previousClose)))
if book.samples == 0
book.sourceStart := source.sourceStart
book.samples += 1
if book.samples <= 14
book.seedSum += tr
book.value := book.samples == 14 ? book.seedSum / 14 : na
else
book.value := (book.value * 13 + tr) / 14
book.previousClose := source.c
book.sourceEnd := source.sourceEnd
else
book.samples := 0
book.seedSum := 0
book.previousClose := na
book.value := na
book.sourceStart := na
book.sourceEnd := na
book
f_camAtrBasisCode(string mode, bool futures, string resolvedDecision) =>
mode == "RTH session" or mode == "Market default" and futures and resolvedDecision != "Exchange day" ? 2 : 1
f_camAtrBasisName(int code) =>
code == 2 ? "Observed RTH sessions · Wilder14" : code == 1 ? "Provider daily · ATR14" : "Unavailable"
f_camAtrReady(float value, int sourceEnd, int cutoff) =>
not na(value) and f_camSign(value) > 0 and not na(sourceEnd) and not na(cutoff) and sourceEnd <= cutoff and cutoff - sourceEnd <= 604800000
f_camAtrFreeze(CamSourcePacket packet, CamAtrBook book, int basis, float providerAtr, int providerStamp, int cutoff) =>
packet.atrBasisCode := basis
packet.atr := basis == 2 ? book.value : providerAtr
packet.atrStamp := basis == 2 ? book.sourceEnd : providerStamp
packet.atrSamples := basis == 2 ? book.samples : not na(providerAtr) ? 14 : 0
packet.atrStart := basis == 2 ? book.sourceStart : na
packet.atrReady := f_camAtrReady(packet.atr, packet.atrStamp, cutoff)
packet
f_camSourceCode(string kind) =>
kind == "RTH" ? 1 : kind == "ETH" ? 2 : kind == "Exchange daily" ? 3 : kind == "Selected session" ? 4 : 0
f_camSourceName(int code) =>
code == 1 ? "RTH" : code == 2 ? "ETH" : code == 3 ? "Exchange daily" : code == 4 ? "Selected session" : "Unavailable"
f_camSourceReason(int code) =>
code == 1 ? "Complete source pair frozen before the opening minute" : code == 2 ? "Auto requires complete prior postmarket/current premarket ending at RTH open; choose a fixed source if unavailable" : code == 3 ? "Two complete chosen H/L and close windows required" : code == 4 ? "Source or independent-close timestamps not ordered" : code == 5 ? "Source or ATR unavailable before decision open" : code == 6 ? "Source or close older than seven elapsed days" : code == 7 ? "Chosen current inner geometry unavailable" : code == 8 ? "Chosen previous inner geometry unavailable" : code == 9 ? "Selected ATR unavailable, warming or stale" : code == 10 ? "Exact valid one-minute opening unavailable" : "Waiting for an observed source opening"
f_camSelectionReason(int code) =>
code == 6 ? "Market default: Exchange daily for crypto; 24/7 provider clock" : code == 7 ? "Market default: RTH for non-crypto; configured cash-session source" : code == 1 ? "Auto crypto: Exchange daily, 24/7 provider clock" : code == 2 ? "Fixed source; no automatic session substitution" : code == 3 ? "ETH selected: pre-opening excursion exceeded selected ATR tolerance" : code == 4 ? "RTH selected: complete pre-opening excursion stayed within tolerance" : "Auto selection lacks complete aligned pre/post windows"
f_camSourceEngine() =>
// Explicit ticker/timeframe in this nested request; every field is completed.
[dh1, dl1, dc1, ds1, de1, dh2, dl2, dc2, ds2, de2, dailyAtr] = request.security(syminfo.tickerid, "D", [high[1], low[1], close[1], time[1], time_close[1], high[2], low[2], close[2], time[2], time_close[2], ta.atr(14)[1]], gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_on, calc_bars_count = 500)
int exchangeStart = time("D")
int exchangeEnd = time_close("D")
bool crypto = syminfo.type == "crypto"
string resolvedDecision = decisionMode == "Auto" ? (crypto ? "Exchange day" : "US cash") : decisionMode
[decisionStart, decisionEnd, decisionAllowed, clockLabel] = f_camClock(time, exchangeStart, exchangeEnd, resolvedDecision, customSession, customTimezone, customWeekdays)
[rthStart, rthEnd, rthAllowed, rthLabel] = f_camClock(time, exchangeStart, exchangeEnd, "Custom", regularSession, regularTimezone, not crypto)
string ethMode = extendedClock == "Auto" and crypto ? "Exchange day" : "Custom"
string ethText = extendedClock == "Custom" ? extendedSession : syminfo.type == "futures" ? "1800-1700" : "0400-2000"
string ethZone = extendedClock == "Custom" ? extendedTimezone : "America/New_York"
[ethStart, ethEnd, ethAllowed, ethLabel] = f_camClock(time, exchangeStart, exchangeEnd, ethMode, ethText, ethZone, not crypto)
// Find the RTH opening belonging to this ETH interval using local calendar dates.
[regularAtEthStart, regularEndAtEthStart, ignoredDay, ignoredLabel] = f_camClock(ethStart, exchangeStart, exchangeEnd, "Custom", regularSession, regularTimezone, not crypto)
int nextRegular = regularAtEthStart
if not na(nextRegular) and nextRegular < ethStart
nextRegular := timestamp(regularTimezone, year(nextRegular, regularTimezone), month(nextRegular, regularTimezone), dayofmonth(nextRegular, regularTimezone) + 1, hour(nextRegular, regularTimezone), minute(nextRegular, regularTimezone))
[pairedRegularStart, pairedRegularEnd, pairedDay, pairedLabel] = f_camClock(nextRegular, exchangeStart, exchangeEnd, "Custom", regularSession, regularTimezone, not crypto)
var CamAtrBook rthAtr = CamAtrBook.new()
var CamWindowBook rth = CamWindowBook.new(CamLevels.new(), CamLevels.new())
var CamWindowBook eth = CamWindowBook.new(CamLevels.new(), CamLevels.new())
var CamWindowBook selected = CamWindowBook.new(CamLevels.new(), CamLevels.new())
var CamWindowBook pre = CamWindowBook.new(CamLevels.new(), CamLevels.new())
var CamWindowBook post = CamWindowBook.new(CamLevels.new(), CamLevels.new())
var CamSourcePacket frozen = CamSourcePacket.new(current = CamLevels.new(), prior = CamLevels.new())
bool overlap = decisionAllowed and time < decisionEnd and time_close > decisionStart
bool newDecision = overlap and (na(frozen.sessionStart) or frozen.sessionStart != decisionStart)
// Certify any missed closing boundary before selecting predecessors.
if not rth.finished and time >= rth.finish
f_camBookFinish(rth, false, "Exact RTH end was not observed")
if not eth.finished and time >= eth.finish
f_camBookFinish(eth, false, "Exact ETH end was not observed")
if not selected.finished and time >= selected.finish
f_camBookFinish(selected, false, "Exact selected-window end was not observed")
// A missed observed closing boundary is consumed before freezing this opening.
f_camAtrConsume(rthAtr, rth.latest)
if newDecision
CamSourcePacket next = CamSourcePacket.new(current = CamLevels.new(), prior = CamLevels.new())
next.sessionStart := decisionStart
next.sessionEnd := decisionEnd
next.cutoff := decisionStart
next.clockLabel := clockLabel
next.openKnown := time == decisionStart and time_close <= decisionEnd and f_camBarValid(open, high, low, close)
next.opening := next.openKnown ? open : na
next.openingTime := next.openKnown ? time : na
int atrBasis = f_camAtrBasisCode(atrBasisMode, syminfo.type == "futures", resolvedDecision)
f_camAtrFreeze(next, rthAtr, atrBasis, dailyAtr, de1, decisionStart)
CamLevels daily1 = f_camLevels(dh1, dl1, dc1, ds1, de1, not na(de1), "Completed provider daily source unavailable")
CamLevels daily2 = f_camLevels(dh2, dl2, dc2, ds2, de2, not na(de2), "Second provider daily source unavailable")
string chosen = sourceMode == "Market default" ? (crypto ? "Exchange daily" : "RTH") : sourceMode == "Auto RTH/ETH" and crypto ? "Exchange daily" : sourceMode
bool selectionReady = true
if chosen == "Auto RTH/ETH"
bool postReady = post.latest.complete and post.latest.sourceStart == rth.latest.sourceEnd and post.latest.sourceEnd <= decisionStart
bool preReady = pre.latest.complete and pre.latest.sourceEnd == decisionStart and pre.latest.sourceStart >= rth.latest.sourceEnd
bool alignedOpening = decisionStart == nextRegular
selectionReady := rth.latest.complete and eth.latest.complete and postReady and preReady and alignedOpening and next.atrReady
next.excursion := selectionReady ? math.max(math.max(post.latest.h, pre.latest.h) - rth.latest.h, rth.latest.l - math.min(post.latest.l, pre.latest.l)) : na
next.excursionThreshold := next.atr * switchingTolerance / 100
bool outside = selectionReady and (f_camCompare(math.max(post.latest.h, pre.latest.h), rth.latest.h + next.excursionThreshold) > 0 or f_camCompare(math.min(post.latest.l, pre.latest.l), rth.latest.l - next.excursionThreshold) < 0)
chosen := outside ? "ETH" : "RTH"
next.selectionReason := not selectionReady ? "Auto requires complete prior postmarket and current premarket ending at the chosen RTH open; choose a fixed source if unavailable" : outside ? "ETH selected: observed excursion exceeded selected ATR tolerance before open" : "RTH selected: complete observed excursion stayed within tolerance at open"
next.selectionCode := not selectionReady ? 5 : outside ? 3 : 4
else
next.selectionCode := sourceMode == "Market default" ? (crypto ? 6 : 7) : sourceMode == "Auto RTH/ETH" and crypto ? 1 : 2
next.selectionReason := f_camSelectionReason(next.selectionCode)
CamLevels main1 = chosen == "RTH" ? rth.latest : chosen == "ETH" ? eth.latest : chosen == "Selected session" ? selected.latest : daily1
CamLevels main2 = chosen == "RTH" ? rth.prior : chosen == "ETH" ? eth.prior : chosen == "Selected session" ? selected.prior : daily2
string chosenClose = closeMode == "Match source" ? chosen : closeMode
CamLevels close1 = chosenClose == "RTH" ? rth.latest : chosenClose == "ETH" ? eth.latest : chosenClose == "Selected session" ? selected.latest : daily1
CamLevels close2 = chosenClose == "RTH" ? rth.prior : chosenClose == "ETH" ? eth.prior : chosenClose == "Selected session" ? selected.prior : daily2
next.sourceKind := chosen + (chosen == "Exchange daily" ? " · provider H/L" : " · observed 1m H/L")
next.closeKind := chosenClose + (chosenClose == "Exchange daily" ? " · provider daily/settlement C" : " · observed last-trade C")
next.sourceCode := f_camSourceCode(chosen)
next.closeCode := f_camSourceCode(chosenClose)
next.closeSourceEnd := close1.sourceEnd
next.priorCloseSourceEnd := close2.sourceEnd
bool completePair = main1.complete and main2.complete and close1.complete and close2.complete
bool ordered = not na(main1.sourceStart) and not na(main1.sourceEnd) and not na(main2.sourceEnd) and main2.sourceEnd <= main1.sourceStart and close2.sourceEnd <= close1.sourceStart
bool available = main1.sourceEnd <= decisionStart and close1.sourceEnd <= decisionStart and next.atrStamp <= decisionStart
bool fresh = not na(main1.sourceEnd) and decisionStart - main1.sourceEnd <= 604800000 and not na(close1.sourceEnd) and decisionStart - close1.sourceEnd <= 604800000
next.current := f_camLevels(main1.h, main1.l, close1.c, main1.sourceStart, main1.sourceEnd, completePair and selectionReady, "Required H/L or selected close predecessor is incomplete", false)
next.prior := f_camLevels(main2.h, main2.l, close2.c, main2.sourceStart, main2.sourceEnd, completePair and selectionReady, "Previous chosen H/L or close source is incomplete", false)
next.ready := selectionReady and completePair and ordered and available and fresh and next.current.valid and next.prior.valid and next.atrReady
next.reason := not selectionReady ? next.selectionReason : not completePair ? "Two complete chosen H/L and close windows required" : not ordered ? "Source or independent-close timestamps not ordered" : not available ? "Source or ATR was unavailable before decision open" : not fresh ? "Source or close is older than seven elapsed days" : not next.current.valid ? next.current.reason : not next.prior.valid ? next.prior.reason : not next.atrReady ? "Selected ATR unavailable, warming or stale" : not next.openKnown ? "Exact valid one-minute opening unavailable" : "Complete source pair frozen before the opening minute"
next.reasonCode := not selectionReady ? 2 : not completePair ? 3 : not ordered ? 4 : not available ? 5 : not fresh ? 6 : not next.current.valid ? 7 : not next.prior.valid ? 8 : not next.atrReady ? 9 : not next.openKnown ? 10 : 1
next.rthSamples := rth.samples
next.ethSamples := eth.samples
next.selectedSamples := selected.samples
next.rthComplete := rth.latest.complete
next.ethComplete := eth.latest.complete
next.selectedComplete := selected.latest.complete
next.coverage := "1m source · RTH " + rth.reason + " / ETH " + eth.reason + "; up to 100,000 source minutes"
frozen := next
// Current minute cannot affect this decision's selected map or auto choice.
f_camObserve(rth, rthStart, rthEnd, rthAllowed, time, time_close, open, high, low, close)
f_camAtrConsume(rthAtr, rth.latest)
f_camObserve(eth, ethStart, ethEnd, ethAllowed, time, time_close, open, high, low, close)
f_camObserve(selected, decisionStart, decisionEnd, decisionAllowed, time, time_close, open, high, low, close)
f_camObserve(pre, ethStart, nextRegular, ethAllowed and pairedDay and ethStart < nextRegular, time, time_close, open, high, low, close)
f_camObserve(post, pairedRegularEnd, ethEnd, ethAllowed and pairedDay and pairedRegularEnd < ethEnd, time, time_close, open, high, low, close)
// Scalar-only transport is essential: returning nested UDTs across 100k
// source minutes exhausted actual TradingView runtime memory in development.
[frozen.current.h, frozen.current.l, frozen.current.c, frozen.current.sourceStart, frozen.current.sourceEnd, frozen.current.complete, frozen.prior.h, frozen.prior.l, frozen.prior.c, frozen.prior.sourceStart, frozen.prior.sourceEnd, frozen.prior.complete, frozen.ready, frozen.openKnown, frozen.opening, frozen.openingTime, frozen.sessionStart, frozen.sessionEnd, frozen.cutoff, frozen.atr, frozen.atrStamp, frozen.closeSourceEnd, frozen.priorCloseSourceEnd, frozen.excursion, frozen.excursionThreshold, frozen.sourceCode, frozen.closeCode, frozen.reasonCode, frozen.selectionCode, frozen.rthSamples, frozen.ethSamples, frozen.selectedSamples, frozen.rthComplete, frozen.ethComplete, frozen.selectedComplete, frozen.atrBasisCode, frozen.atrSamples, frozen.atrStart]
// Modify only session selection; retain the chart ticker's other price modifiers.
string camSourceTicker = ticker.modify(syminfo.tickerid, session = session.extended)
[camRawH1, camRawL1, camRawC1, camRawStart1, camRawEnd1, camRawComplete1, camRawH2, camRawL2, camRawC2, camRawStart2, camRawEnd2, camRawComplete2, camRawReady, camRawOpenKnown, camRawOpen, camRawOpenTime, camRawSessionStart, camRawSessionEnd, camRawCutoff, camRawAtr, camRawAtrStamp, camRawCloseEnd1, camRawCloseEnd2, camRawExcursion, camRawExcursionThreshold, camRawSourceCode, camRawCloseCode, camRawReasonCode, camRawSelectionCode, camRawRthSamples, camRawEthSamples, camRawSelectedSamples, camRawRthComplete, camRawEthComplete, camRawSelectedComplete, camRawAtrBasisCode, camRawAtrSamples, camRawAtrStart] = request.security(camSourceTicker, "1", f_camSourceEngine(), gaps = barmerge.gaps_off, lookahead = barmerge.lookahead_off, calc_bars_count = 100000)
var CamSourcePacket camSourcePacket = CamSourcePacket.new(current = CamLevels.new(), prior = CamLevels.new())
int camExchangeStart = time("D")
int camExchangeEnd = time_close("D")
string camResolvedDecision = decisionMode == "Auto" ? (syminfo.type == "crypto" ? "Exchange day" : "US cash") : decisionMode
[camScheduledStart, camScheduledEnd, camDayAllowed, camClockLabel] = f_camClock(time, camExchangeStart, camExchangeEnd, camResolvedDecision, customSession, customTimezone, customWeekdays)
bool camChartSupported = chart.is_standard and timeframe.isminutes and timeframe.multiplier >= 1 and timeframe.multiplier <= 60
bool camOverlapNow = camChartSupported and camDayAllowed and not na(time_close) and time < camScheduledEnd and time_close > camScheduledStart
bool camContainedNow = camOverlapNow and time >= camScheduledStart and time_close <= camScheduledEnd
bool camBarOhlcValid = f_camBarValid(open, high, low, close)
float camSma = ta.sma(close, smaLength)
var CamLevels camMap = CamLevels.new()
var CamLevels camPriorMap = CamLevels.new()
var int camSessionStart = na
var int camSessionEnd = na
var int camOpenTime = na
var float camOpen = na
var bool camOpenKnown = false
var bool camMapReady = false
var bool camSourceStale = false
var string camDataReason = "Waiting for first fully contained decision candle"
var string camRelationship = "Unavailable"
var string camWidthLabel = "Unavailable"
var string camOpenLocation = "OPEN UNKNOWN"
var string camDecisionLabel = "Decision window not observed"
var string camSourceLabel = "Source not frozen"
var string camCloseLabel = "Close source not frozen"
var string camSourceSelectionReason = "Waiting for source cutoff"
var int camSourceCutoff = na
var int camOpeningSourceTime = na
var int camAtrStamp = na
var float camAtrReference = na
var string camAtrBasis = "Unavailable"
var bool camAtrReady = false
var int camAtrSamples = 0
var int camAtrStart = na
var float camRangeDelta = na
var float camRangeThreshold = na
var int camBias = 0
var float camWidthRatio = na
var int camClosedBar = na
var int camClosedTime = na
var float camClosedPrice = na
var bool camInSession = false
var float camPreviousClose = na
var string camSelectedCoverage = "Waiting for 1m source snapshot"
var int camSelectedCount = 0
var int camActiveCount = 0
var int camHigherActive = 0
var int camLowerActive = 0
var int camCandidateCount = 0
var int camApproachingCount = 0
var bool camSessionEndedOnce = false
var int camPreviousObservedTime = na
var int camPreviousObservedClose = na
bool camSessionStartPulse = false
bool camSessionEndPulse = false
bool camActivated = false
bool camEnded = false
bool camArmed = false
int camActivationCount = 0
int camEndingCount = 0
if array.size(plays) == 0
for i = 0 to 13
array.push(plays, f_camNewPlay(i, camMap, false, "Waiting for supported chart and source opening", 0, na, false, na))
if barstate.isconfirmed
camClosedBar := bar_index
camClosedTime := time_close
camClosedPrice := camBarOhlcValid ? close : na
array.clear(camBarEvents)
if not camChartSupported
camDataReason := "Unsupported chart · use standard minute candles, 1m through 60m"
camDecisionLabel := "UNSUPPORTED CHART"
camSelectedCoverage := "DISABLED · unsupported chart"
bool newDecision = camContainedNow and (na(camSessionStart) or camScheduledStart != camSessionStart)
if newDecision
for i = 0 to 13
Play old = array.get(plays, i)
if old.state == "ACTIVE" or old.state == "ARMED"
f_camEnd(old, "EXPIRED", "Late-observed session rollover · no unobserved closing fill inferred", na, bar_index, time_close, false)
camSessionStartPulse := true
camSessionStart := camScheduledStart
camSessionEnd := camScheduledEnd
camSessionEndedOnce := false
// Reconstruct cross-context data once per decision, never once per minute.
CamSourcePacket admitted = CamSourcePacket.new(current = f_camLevels(camRawH1, camRawL1, camRawC1, camRawStart1, camRawEnd1, camRawComplete1, "Incomplete current source", false), prior = f_camLevels(camRawH2, camRawL2, camRawC2, camRawStart2, camRawEnd2, camRawComplete2, "Incomplete previous source", false))
admitted.ready := camRawReady
admitted.openKnown := camRawOpenKnown
admitted.opening := camRawOpen
admitted.openingTime := camRawOpenTime
admitted.sessionStart := camRawSessionStart
admitted.sessionEnd := camRawSessionEnd
admitted.cutoff := camRawCutoff
admitted.atr := camRawAtr
admitted.atrStamp := camRawAtrStamp
admitted.atrBasisCode := camRawAtrBasisCode
admitted.atrSamples := camRawAtrSamples
admitted.atrStart := camRawAtrStart
admitted.atrReady := f_camAtrReady(camRawAtr, camRawAtrStamp, camRawCutoff)
admitted.closeSourceEnd := camRawCloseEnd1
admitted.priorCloseSourceEnd := camRawCloseEnd2
admitted.excursion := camRawExcursion
admitted.excursionThreshold := camRawExcursionThreshold
admitted.sourceCode := camRawSourceCode
admitted.closeCode := camRawCloseCode
admitted.reasonCode := camRawReasonCode
admitted.selectionCode := camRawSelectionCode
admitted.sourceKind := f_camSourceName(camRawSourceCode) + (camRawSourceCode == 3 ? " · provider H/L" : " · observed 1m H/L")
admitted.closeKind := f_camSourceName(camRawCloseCode) + (camRawCloseCode == 3 ? " · provider daily/settlement C" : " · observed last-trade C")
admitted.reason := f_camSourceReason(camRawReasonCode)
admitted.selectionReason := f_camSelectionReason(camRawSelectionCode)
admitted.rthSamples := camRawRthSamples
admitted.ethSamples := camRawEthSamples
admitted.selectedSamples := camRawSelectedSamples
admitted.rthComplete := camRawRthComplete
admitted.ethComplete := camRawEthComplete
admitted.selectedComplete := camRawSelectedComplete
admitted.coverage := "1m source · RTH " + (camRawRthComplete ? "complete " : "incomplete ") + str.tostring(camRawRthSamples) + " bars; ETH " + (camRawEthComplete ? "complete " : "incomplete ") + str.tostring(camRawEthSamples) + "; up to 100,000 source minutes"
camSourcePacket := admitted
bool packetMatches = not na(camSourcePacket) and camSourcePacket.sessionStart == camSessionStart and camSourcePacket.cutoff == camSessionStart and camSourcePacket.cutoff <= time
bool chartOpeningObserved = time == camSessionStart or not na(camPreviousObservedTime) and camPreviousObservedTime <= camSessionStart and camPreviousObservedClose >= camSessionStart
camMap := packetMatches ? camSourcePacket.current : CamLevels.new()
camPriorMap := packetMatches ? camSourcePacket.prior : CamLevels.new()
camOpenKnown := packetMatches and camSourcePacket.openKnown and chartOpeningObserved
camOpen := camOpenKnown ? camSourcePacket.opening : na
camOpenTime := camOpenKnown ? camSourcePacket.openingTime : na
camOpeningSourceTime := camOpenTime
camMapReady := packetMatches and camSourcePacket.ready
camSourceCutoff := packetMatches ? camSourcePacket.cutoff : na
camAtrReference := packetMatches ? camSourcePacket.atr : na
camAtrStamp := packetMatches ? camSourcePacket.atrStamp : na
camAtrBasis := packetMatches ? f_camAtrBasisName(camSourcePacket.atrBasisCode) : "Unavailable"
camAtrReady := packetMatches and camSourcePacket.atrReady
camAtrSamples := packetMatches ? camSourcePacket.atrSamples : 0
camAtrStart := packetMatches ? camSourcePacket.atrStart : na
camSourceLabel := packetMatches ? camSourcePacket.sourceKind : "Source packet unavailable at this chart opening"
camCloseLabel := packetMatches ? camSourcePacket.closeKind : "Unavailable"
camSourceSelectionReason := packetMatches ? camSourcePacket.selectionReason : "No matching session snapshot admitted"
camSelectedCoverage := packetMatches ? camSourcePacket.coverage : "Source history unavailable or packet identity differs"
camDecisionLabel := camClockLabel
camDataReason := not packetMatches ? "Matching frozen source unavailable at first contained chart candle" : not camSourcePacket.ready ? camSourcePacket.reason : not chartOpeningObserved ? "Chart history starts after the opening boundary · earlier bad-price blocks are unknown" : not camOpenKnown ? "Exact valid source opening unavailable" : camSourcePacket.reason
camSourceStale := not na(camMap.sourceEnd) and camSessionStart - camMap.sourceEnd > 604800000
[relation, geometricBias, geometricRatio, geometricWidth] = f_camRelationship(camMap, camPriorMap)
camRelationship := camMapReady ? relation : "Unavailable"
[newBias, ratio, widthLabel, rangeOk, widthOk, openOk, regionOk, blocked, candidate, approaching] = f_camPrerequisites("HA", camMap, camPriorMap, camAtrReference, camOpen, camOpen, camOpen, false, rangeThresholdPct, widthThresholdPct, allowNeutral)
camBias := camMapReady ? newBias : 0
camWidthRatio := camMapReady ? ratio : na
camWidthLabel := camMapReady ? widthLabel : "Unavailable"
camRangeDelta := camMapReady ? camMap.c - camPriorMap.c : na
camRangeThreshold := camAtrReference * rangeThresholdPct / 100
camOpenLocation := f_camOpenLocation(camOpen, camMap)
camPreviousClose := camOpen
for i = 0 to 13
Play p = f_camNewPlay(i, camMap, camMapReady, camDataReason, camBias, camOpen, camOpenKnown, camSessionStart)
p.sourceKind := camSourceLabel
p.closeKind := camCloseLabel
p.atrUsed := camAtrReference
p.atrStamp := camAtrStamp
p.atrBasis := camAtrBasis
p.atrSamples := camAtrSamples
p.atrStart := camAtrStart
p.sourceCutoff := camSourceCutoff
p.closeSourceEnd := packetMatches ? camSourcePacket.closeSourceEnd : na
p.stopMode := stopModel
p.targetMode := targetModel
// The source opening is the first observed OPEN, including on 60m charts.
if camOpenKnown
f_camRefreshPlay(p, camMap, camPriorMap, camAtrReference, camOpen, camOpen, camOpen, true, true, camMapReady, camDataReason, true)
array.set(plays, i, p)
camInSession := camContainedNow and not na(camSessionStart) and camScheduledStart == camSessionStart
bool sessionEnded = not na(camSessionEnd) and time_close >= camSessionEnd
if sessionEnded and not camSessionEndedOnce
camSessionEndPulse := true
camSessionEndedOnce := true
for i = 0 to 13
Play p = array.get(plays, i)
bool smaReady = not smaGate or not na(camSma) and bar_index >= smaLength - 1 and f_camCompare(close, camSma) * p.dir > 0
f_camRefreshPlay(p, camMap, camPriorMap, camAtrReference, camOpen, open, close, camBarOhlcValid, camInSession, camMapReady and camOpenKnown, camDataReason, smaReady)
f_camRearm(p, bar_index, camInSession and not sessionEnded, allowRearm, cooldownBars, maxEpisodes)
f_camStep(p, camMap, open, high, low, close, camPreviousClose, bar_index, time_close, camInSession, sessionEnded, camWidthLabel == "Wide", syminfo.mintick, setupDeadline, activeDeadline, stopBufferTicks, minRewardR)
if camInSession
camPreviousClose := camBarOhlcValid ? close : na
camActiveCount := 0
camHigherActive := 0
camLowerActive := 0
camCandidateCount := 0
camApproachingCount := 0
for i = 0 to 13
Play p = array.get(plays, i)
camActiveCount += p.state == "ACTIVE" ? 1 : 0
camHigherActive += p.state == "ACTIVE" and p.biasRequired == 1 ? 1 : 0
camLowerActive += p.state == "ACTIVE" and p.biasRequired == -1 ? 1 : 0
camCandidateCount += p.candidate ? 1 : 0
camApproachingCount += p.approaching ? 1 : 0
if array.size(camBarEvents) > 0
for i = 0 to array.size(camBarEvents) - 1
CamEvent e = array.get(camBarEvents, i)
camActivationCount += e.kind == "ACTIVE" ? 1 : 0
camEndingCount += f_camTerminal(e.kind) ? 1 : 0
camArmed := camArmed or e.kind == "ARMED"
camActivated := camActivationCount > 0
camEnded := camEndingCount > 0
camPreviousObservedTime := time
camPreviousObservedClose := time_close
f_camJsonString(string value) =>
string escaped = str.replace_all(value, "\\", "\\\\")
escaped := str.replace_all(escaped, "\"", "\\\"")
escaped := str.replace_all(escaped, "\n", "\\n")
escaped := str.replace_all(escaped, "\t", "\\t")
"\"" + escaped + "\""
f_camJsonNumber(float value) =>
f_camNumber(value)
f_camJsonTime(int value) =>
na(value) ? "null" : str.tostring(value)
f_camJsonBool(bool value) =>
value ? "true" : "false"
f_camSettingsId() =>
CAM_FORMULA + "|lite2-original2.1|" + atrBasisMode + "|" + sourceMode + "|" + closeMode + "|" + decisionMode + "|" + customSession + "|" + customTimezone + "|" + f_camJsonBool(customWeekdays) + "|" + regularSession + "|" + regularTimezone + "|" + extendedClock + "|" + extendedSession + "|" + extendedTimezone + "|" + f_camJsonNumber(switchingTolerance) + "|" + f_camJsonNumber(rangeThresholdPct) + "|" + f_camJsonNumber(widthThresholdPct) + "|" + f_camJsonBool(allowNeutral) + "|" + f_camJsonBool(smaGate) + "|" + str.tostring(smaLength) + "|" + f_camJsonBool(enableA) + f_camJsonBool(enableB) + f_camJsonBool(enableC) + f_camJsonBool(enableD) + f_camJsonBool(enableE) + f_camJsonBool(enableF) + f_camJsonBool(enableG) + "|" + str.tostring(setupDeadline) + "|" + str.tostring(activeDeadline) + "|" + str.tostring(stopBufferTicks) + "|" + f_camJsonNumber(minRewardR) + "|" + stopModel + "|" + f_camJsonNumber(stopAtrMultiple) + "|" + targetModel + "|" + f_camJsonNumber(targetAtrMultiple) + "|" + f_camJsonNumber(maxTargetAtr) + "|" + f_camJsonBool(allowRearm) + "|" + str.tostring(cooldownBars) + "|" + str.tostring(maxEpisodes)
f_camEventJson(CamEvent e) =>
string id = syminfo.tickerid + "|" + timeframe.period + "|" + f_camSettingsId() + "|" + str.tostring(e.sessionStart) + "|" + e.code + "|" + str.tostring(e.episode) + "|" + e.kind + "|" + str.tostring(e.eventTime)
"{\"id\":" + f_camJsonString(id) + ",\"code\":" + f_camJsonString(e.code) + ",\"episode\":" + str.tostring(e.episode) + ",\"type\":" + f_camJsonString(e.kind) + ",\"direction\":" + f_camJsonString(e.dir == 1 ? "long" : "short") + ",\"time\":" + f_camJsonTime(e.eventTime) + ",\"session_start\":" + f_camJsonTime(e.sessionStart) + ",\"source_start\":" + f_camJsonTime(e.sourceStart) + ",\"source_close\":" + f_camJsonTime(e.sourceEnd) + ",\"source_kind\":" + f_camJsonString(e.sourceKind) + ",\"close_kind\":" + f_camJsonString(e.closeKind) + ",\"selected_close_time\":" + f_camJsonTime(e.closeSourceEnd) + ",\"source_cutoff\":" + f_camJsonTime(e.sourceCutoff) + ",\"atr_time\":" + f_camJsonTime(e.atrStamp) + ",\"atr_basis\":" + f_camJsonString(e.atrBasis) + ",\"atr_value\":" + f_camJsonNumber(e.atrUsed) + ",\"atr_samples\":" + str.tostring(e.atrSamples) + ",\"atr_start\":" + f_camJsonTime(e.atrStart) + ",\"price\":" + f_camJsonNumber(e.price) + ",\"entry\":" + f_camJsonNumber(e.entry) + ",\"stop\":" + f_camJsonNumber(e.stop) + ",\"target\":" + f_camJsonNumber(e.target) + ",\"ambiguous\":" + f_camJsonBool(e.ambiguous) + ",\"reason\":" + f_camJsonString(e.reason) + "}"
if barstate.isconfirmed and array.size(camBarEvents) > 0
string selectedEvents = ""
int selectedCount = 0
for i = 0 to array.size(camBarEvents) - 1
CamEvent e = array.get(camBarEvents, i)
bool include = e.kind == "ACTIVE" ? alertActivation : e.kind == "ARMED" ? alertArmed : alertEnding and f_camTerminal(e.kind)
if include
selectedEvents += (selectedCount > 0 ? "," : "") + f_camEventJson(e)
selectedCount += 1
if selectedCount > 0
string envelope = "{\"schema\":\"camarilla-desk.v2\",\"product_version\":" + f_camJsonString(CAM_VERSION) + ",\"rule_version\":\"lite2-prerequisites.original2.1-execution\",\"formula\":" + f_camJsonString(CAM_FORMULA) + ",\"settings_id\":" + f_camJsonString(f_camSettingsId()) + ",\"source_mode\":" + f_camJsonString(sourceMode) + ",\"decision_mode\":" + f_camJsonString(decisionMode) + ",\"symbol\":" + f_camJsonString(syminfo.tickerid) + ",\"timeframe\":" + f_camJsonString(timeframe.period) + ",\"confirmed_time\":" + f_camJsonTime(time_close) + ",\"fill_confirmed\":false,\"events\":[" + selectedEvents + "]}"
alert(envelope, alert.freq_once_per_bar_close)
// VIEW CONTRACT v2: tracked immutable plans are independent from manual inspection.
// Chart-first Ribbon and complete seven-level map. Clean adds plan detail.
// Display inputs do not mutate candidates, episodes, alerts or reference prices.
string vBoardMode = input.string("Ribbon", "Trade HUD", options = ["Ribbon", "Clean", "Context", "Audit", "Off"], group = "Display", tooltip = "Ribbon: compact source, context and current action. Clean: expanded tracked plan. Context: up to five attention rows. Audit: all fourteen plays. Attention priority is not a strength score.", display = display.none)
string vDetail = input.string("Off", "Inspector", options = ["Off", "Brief", "Standard"], group = "Display", tooltip = "Inspect any play without replacing the independently tracked chart plan. Standard adds prerequisites and source evidence.", display = display.none)
string vTheme = input.string("Trading Dark", "Theme", options = ["Trading Dark", "Accessible Dark", "Accessible Light", "Aurora", "Royal Gold"], group = "Display", display = display.none)
string vChartMode = input.string("Map", "Chart density", options = ["Map", "Clean", "Context", "Audit", "Off"], group = "Display", tooltip = "Map: complete seven-level map plus fixed plan and episode markers. Clean: selected plan/trigger only. Context: map and optional middle band. Audit: adds bounded event observations. Off hides drawings only.", display = display.none)
string vScale = input.string("Medium", "Panel & chart tag size", options = ["Small", "Medium", "Large"], group = "Display", active = CAM_IS_LAB or vBoardMode != "Off" or vDetail != "Off" or vChartMode != "Off", tooltip = "Also controls Strategy Lab readout size.", display = display.none)
string vFocusCode = input.string("Auto", "Inspect play", options = ["Auto", "HA", "LA", "HB", "LB", "HC", "LC", "HD", "LD", "HE", "LE", "HF", "LF", "HG", "LG"], group = "Display", active = vDetail != "Off", tooltip = "Auto: active, armed, approaching, then candidate, with fixed-code ties. A manual code changes only the Inspector. H/L describes context, not trade direction.", display = display.none)
bool vCodes = input.bool(false, "Show play codes", group = "Display", display = display.none)
string vBoardCorner = input.string("Top Left", "Trade HUD position", options = ["Top Left", "Top Right", "Bottom Left", "Bottom Right"], group = "Display · placement", active = vBoardMode != "Off", display = display.none)
string vFocusCorner = input.string("Bottom Left", "Inspector position", options = ["Top Left", "Top Right", "Bottom Left", "Bottom Right"], group = "Display · placement", active = vDetail != "Off", tooltip = "Audit reserves its entire column, so the Inspector moves to the other side. Other HUD modes use the free corner on the same side first. Hidden panels reserve no anchor.", display = display.none)
int vHeaderClearance = input.int(12, "Top Left header clearance · %", minval = 0, maxval = 20, group = "Display · placement", tooltip = "Transparent space beneath the native legend; zero if hidden. Bottom Left has a transparent footer above the watermark. These are layout heuristics.", display = display.none)
bool vPriceTags = input.bool(true, "Price tags", group = "Display · chart", active = vChartMode != "Off", tooltip = "Displaced tags keep true-price leaders and unchanged values. Tracked references receive first priority.", display = display.none)
bool vMiddleBand = input.bool(false, "Camarilla middle band", group = "Display · chart", active = vChartMode == "Context" or vChartMode == "Audit", tooltip = "L2–H2 Camarilla band, not floor-pivot CPR.", display = display.none)
bool vPlanZones = input.bool(true, "Reference risk / reward zones", group = "Display · chart", active = vChartMode != "Off", tooltip = "Frozen reference geometry, not fills or returns. Ended geometry stops at its recorded end time.", display = display.none)
int vHistory = input.int(2, "Ended plans shown", minval = 0, maxval = 8, group = "Display · chart", active = vChartMode != "Off", tooltip = "Newest ended snapshots within visible time and nearby price context. Historical prices are frozen. Zero hides ended geometry.", display = display.none)
int vTagCap = input.int(12, "Chart tag budget", minval = 3, maxval = 24, group = "Display · chart", active = vChartMode != "Off" and vPriceTags, tooltip = "Tracked entry, stop and target come first. Default covers the seven map tags plus tracked references. Episode markers use a separate bounded pool.", display = display.none)
string vTagSide = input.string("Right", "Price tag side", options = ["Right", "Left"], group = "Display · chart", active = vChartMode != "Off" and vPriceTags, tooltip = "Right aligns solid price tags beside the current chart. Left restores interior labels. Colliding labels retain their printed prices and true-price leaders.", display = display.none)
bool vEpisodeMarks = input.bool(true, "Entry / ending markers", group = "Display · chart", active = vChartMode != "Off", tooltip = "Local arrows mark confirmed reference entries. Ending labels show TARGET, STOP or EXPIRED at the recorded observation, not broker fills. A ? suffix discloses ambiguous OHLC paths.", display = display.none)
int vZoneOpacity = input.int(14, "Plan zone opacity · %", minval = 4, maxval = 35, group = "Display · chart", active = vChartMode != "Off" and vPlanZones, tooltip = "Visual contrast only. It does not change price references, signal logic or results.", display = display.none)
bool vLight = vTheme == "Accessible Light"
color vBg = vLight ? color.rgb(246, 248, 252) : vTheme == "Royal Gold" ? color.rgb(21, 23, 28) : color.rgb(16, 24, 38)
color vStripe = vLight ? color.rgb(233, 239, 247) : vTheme == "Royal Gold" ? color.rgb(32, 35, 43) : color.rgb(23, 35, 52)
color vHeader = vLight ? color.rgb(220, 230, 242) : vTheme == "Royal Gold" ? color.rgb(46, 43, 36) : color.rgb(30, 47, 68)
color vText = vLight ? color.rgb(24, 36, 53) : color.rgb(236, 243, 251)
color vMuted = vLight ? color.rgb(82, 98, 118) : color.rgb(168, 184, 204)
color vAccent = vTheme == "Trading Dark" ? color.rgb(105, 164, 255) : vTheme == "Royal Gold" ? color.rgb(226, 197, 137) : vTheme == "Aurora" ? color.rgb(190, 187, 237) : vLight ? color.rgb(39, 68, 100) : color.rgb(205, 225, 246)
color vUp = vTheme == "Trading Dark" ? color.rgb(66, 190, 133) : vLight ? color.rgb(0, 105, 165) : vTheme == "Aurora" ? color.rgb(95, 207, 239) : color.rgb(92, 184, 239)
color vDown = vTheme == "Trading Dark" ? color.rgb(240, 101, 119) : vLight ? color.rgb(151, 85, 0) : color.rgb(237, 171, 76)
color vFrame = vLight ? color.rgb(184, 200, 221) : color.rgb(53, 71, 93)
int vBodySize = vScale == "Small" ? 12 : vScale == "Large" ? 16 : 14
int vSmallSize = vScale == "Small" ? 10 : vScale == "Large" ? 14 : 12
int vTitleSize = vScale == "Small" ? 14 : vScale == "Large" ? 18 : 16
int vTagSize = vScale == "Small" ? 10 : vScale == "Large" ? 14 : 12
f_vWrap(string value, int width, int maxLines) =>
string result = ""
string current = ""
int rows = 1
bool truncated = false
for word in str.split(value, " ")
if not truncated
if str.length(current) + str.length(word) + 1 > width and str.length(current) > 0
if rows < maxLines
result += current + "\n"
current := word
rows += 1
else
truncated := true
else
current += (str.length(current) > 0 ? " " : "") + word
result + current + (truncated ? "…" : "")
f_vPosition(string choice) =>
switch choice
"Top Left" => position.top_left
"Bottom Left" => position.bottom_left
"Bottom Right" => position.bottom_right
"Top Center" => position.top_center
"Bottom Center" => position.bottom_center
"Middle Left" => position.middle_left
"Middle Right" => position.middle_right
"Middle Center" => position.middle_center
=> position.top_right
// Public view API for the Lab tail: occupied anchors include the entire column
// of a tall Audit HUD, so downstream panels cannot overlap its fourteen rows.
f_vFree(array<string> used, string preferred) =>
string result = preferred
if array.includes(used, result)
string paired = preferred == "Top Left" ? "Bottom Left" : preferred == "Bottom Left" ? "Top Left" : preferred == "Top Right" ? "Bottom Right" : "Top Right"
string across = preferred == "Top Left" ? "Top Right" : preferred == "Bottom Left" ? "Bottom Right" : preferred == "Top Right" ? "Top Left" : "Bottom Left"
array<string> order = array.from(paired, across, "Top Left", "Bottom Left", "Top Right", "Bottom Right", "Middle Left", "Middle Right", "Top Center", "Bottom Center", "Middle Center")
for candidate in order
if not array.includes(used, candidate)
result := candidate
break
array.push(used, result)
result
f_vReserveColumn(array<string> used, string anchor) =>
bool left = str.contains(anchor, "Left")
array<string> column = left ? array.from("Top Left", "Bottom Left", "Middle Left") : array.from("Top Right", "Bottom Right", "Middle Right")
for reserved in column
if not array.includes(used, reserved)
array.push(used, reserved)
true
var array<string> vUsedAnchors = array.new<string>()
var string vBoardAnchor = ""
var string vFocusAnchor = ""
if barstate.isfirst
if vBoardMode != "Off"
vBoardAnchor := f_vFree(vUsedAnchors, vBoardCorner)
if vBoardMode == "Audit"
f_vReserveColumn(vUsedAnchors, vBoardAnchor)
if vDetail != "Off"
vFocusAnchor := f_vFree(vUsedAnchors, vFocusCorner)
f_vTime(int stamp) =>
na(stamp) ? "—" : str.format_time(stamp, "MM-dd HH:mm", "UTC")
f_vDate(int stamp) =>
na(stamp) ? "—" : str.format_time(stamp, "yyyy-MM-dd", "UTC")
f_vClock(int stamp) =>
na(stamp) ? "—" : str.format_time(stamp, "HH:mm", "UTC")
f_vSourceShort(string value) =>
str.contains(value, "SYNTHETIC") ? "TEST RTH" : str.contains(value, "RTH") ? "RTH" : str.contains(value, "ETH") ? "ETH" : str.contains(value, "daily") ? "Daily" : str.contains(value, "Selected") ? "Custom" : "Waiting"
f_vClockShort(string value) =>
str.contains(value, "SYNTHETIC") ? "TEST" : str.contains(value, "Exchange") ? "Exchange day" : str.contains(value, "cash") ? "US cash" : "Custom"
f_vInk(color tint) =>
color.r(tint) * 0.2126 + color.g(tint) * 0.7152 + color.b(tint) * 0.0722 > 105 ? color.rgb(10, 18, 28) : color.white
f_vDirection(int direction) =>
direction == 1 ? "LONG" : direction == -1 ? "SHORT" : "—"
f_vState(Play play, bool inWindow) =>
play.state == "WATCH" ? not inWindow ? "WAIT SESSION" : not camBarOhlcValid ? "DATA WAIT" : not play.candidate ? "BLOCKED" : play.approaching ? "APPROACHING" : "CANDIDATE" : play.state
f_vStateColor(string state, int direction) =>
state == "ACTIVE" ? (direction == 1 ? vUp : vDown) : state == "ARMED" or state == "APPROACHING" ? vAccent : state == "STOP" or state == "INVALIDATED" or state == "REJECTED" ? vDown : state == "TARGET" ? vUp : vMuted
f_vShortName(string family, int direction) =>
switch family
"A" => direction == 1 ? "L3 traverse" : "H3 traverse"
"B" => direction == 1 ? "H4 break & retest" : "L4 break & retest"
"C" => "Opening continuation"
"D" => "Context-opposed continuation"
"E" => direction == 1 ? "L4 failure" : "H4 failure"
"F" => "Outer-tier rejection"
"G" => "Opposite-side rejection"
=> "Session guide"
f_vName(Play play) =>
(vCodes ? play.code + " · " : "") + f_vShortName(play.family, play.dir)
f_vSnapshotName(CamPlanSnapshot plan) =>
(vCodes ? plan.code + " · " : "") + f_vShortName(str.substring(plan.code, 1), plan.dir)
f_vAge(Play play) =>
int start = play.state == "ACTIVE" ? play.activationBar : play.state == "ARMED" ? play.setupBar : na
na(start) or na(camClosedBar) ? "" : str.tostring(math.max(0, camClosedBar - start)) + " bars"
f_vTrigger(Play play) =>
na(play.triggerPrice) ? "No close trigger yet" : "close " + play.triggerOperator + " " + f_camReadable(play.triggerPrice)
f_vPlayTip(Play play) =>
play.code + " · " + play.name + "\n" + f_vDirection(play.dir) + " · " + f_vState(play, camInSession) + "\nWHY: " + play.reason + "\nNEXT: " + play.next + "\nFAIL: " + play.invalidation + "\nClose trigger: " + f_vTrigger(play) + "\nSetup / activation UTC: " + f_vTime(play.setupTime) + " / " + f_vTime(play.activationTime)
f_vGateWords(Play play) =>
"Range " + (play.rangeOk ? "PASS" : "BLOCK") + " · Width " + (play.widthOk ? "PASS" : "BLOCK") + "\nOpening " + (play.openOk ? "PASS" : "BLOCK") + " · Region " + (play.regionOk ? "IN" : "OUT") + "\nSMA " + (not smaGate ? "OFF" : play.smaOk ? "PASS" : "BLOCK")
f_vTrackedIndex() =>
int found = -1
if array.size(camPlans) > 0
for i = 0 to array.size(camPlans) - 1
CamPlanSnapshot plan = array.get(camPlans, i)
if plan.state == "ACTIVE"
found := i
break
found
f_vFallbackFocus() =>
int selected = f_camAutoFocus()
if selected < 0 and camInSession and array.size(plays) > 0
for priority = 0 to 2
if selected < 0
for i = 0 to array.size(plays) - 1
Play play = array.get(plays, i)
bool match = priority == 0 ? play.state == "ARMED" : priority == 1 ? play.state == "WATCH" and play.approaching : play.state == "WATCH" and play.candidate
if match
selected := i
break
selected
f_vRows(bool audit) =>
array<int> rows = array.new<int>()
if array.size(plays) > 0
if audit
for i = 0 to array.size(plays) - 1
array.push(rows, i)
else
for priority = 0 to 3
for i = 0 to array.size(plays) - 1
Play play = array.get(plays, i)
bool match = priority == 0 ? play.state == "ACTIVE" : priority == 1 ? play.state == "ARMED" : priority == 2 ? play.state == "WATCH" and play.approaching : play.state == "WATCH" and play.candidate
if match and array.size(rows) < 5 and not array.includes(rows, i)
array.push(rows, i)
rows
f_vRecovery() =>
not camChartSupported ? "Use standard, time-based candles from 1 to 60 minutes." : not camOpenKnown ? "Load the observed decision opening and complete prior source windows." : not camMapReady ? camDataReason : not camInSession ? "Wait for the selected decision window. The last committed map remains inspectable." : "No qualifying setup. Inspect Audit for each prerequisite."
f_vSourceTip() =>
"PUBLIC CONTEXT\nSource: " + camSourceLabel + " · close: " + camCloseLabel + "\n" + camSourceSelectionReason + "\nDecision: " + camDecisionLabel + "\nMap source UTC: " + f_vTime(camMap.sourceStart) + " → " + f_vTime(camMap.sourceEnd) + "\nSelection cutoff: " + f_vTime(camSourceCutoff) + "\nObserved 1m opening: " + f_vTime(camOpeningSourceTime) + " @ " + f_camExact(camOpen) + "\nH/L/C: " + f_camExact(camMap.h) + " / " + f_camExact(camMap.l) + " / " + f_camExact(camMap.c) + "\n" + camDataReason
f_vRiskTip() =>
"ORIGINAL EXECUTION\nMinimum R: " + str.tostring(minRewardR) + " · buffer " + str.tostring(stopBufferTicks) + " ticks\nStop: " + stopModel + " · ATR " + str.tostring(stopAtrMultiple) + "\nTarget: " + targetModel + " · ATR " + str.tostring(targetAtrMultiple) + "\nMax target ATR: " + (maxTargetAtr == 0 ? "OFF" : str.tostring(maxTargetAtr)) + "\nSetup / active limits: " + str.tostring(setupDeadline) + " / " + str.tostring(activeDeadline) + " bars\nRearm: " + (allowRearm ? "ON · cooldown " + str.tostring(cooldownBars) + " · cap " + str.tostring(maxEpisodes) : "OFF") + "\nChart SMA: " + (smaGate ? str.tostring(smaLength) : "OFF") + "\nReference plans, not broker fills or a validated edge."
type CamViewRow
string key = ""
string value = ""
string tip = ""
int kind = 0
f_vAdd(array<CamViewRow> rows, string key, string value, string tip, int kind = 0) =>
array.push(rows, CamViewRow.new(key, value, tip, kind))
f_vSignature(array<CamViewRow> rows) =>
string result = ""
for row in rows
result += str.tostring(row.kind)
result
f_vInsetStart(array<CamViewRow> rows, string anchor) =>
if anchor == "Top Left" and vHeaderClearance > 0
f_vAdd(rows, "", "", "Transparent chart-header clearance", 5)
f_vInsetEnd(array<CamViewRow> rows, string anchor) =>
if anchor == "Bottom Left"
f_vAdd(rows, "", "", "Transparent watermark clearance", 5)
f_vRender(table panel, array<CamViewRow> rows, string anchor) =>
table.clear(panel, 0, 0, 1, array.size(rows) - 1)
for i = 0 to array.size(rows) - 1
CamViewRow row = array.get(rows, i)
color background = row.kind == 5 ? color.new(vBg, 100) : row.kind == 2 ? vHeader : row.kind == 4 ? vStripe : vBg
if row.kind == 1
table.cell(panel, 0, i, row.key, text_color = vMuted, bgcolor = background, text_size = vSmallSize, text_halign = text.align_left, text_font_family = font.family_default, tooltip = row.tip)
table.cell(panel, 1, i, row.value, text_color = vText, bgcolor = background, text_size = vBodySize, text_halign = text.align_right, text_font_family = font.family_monospace, text_formatting = text.format_bold, tooltip = row.tip)
else
// clear() may remove merges: reapply before every full-row write.
table.merge_cells(panel, 0, i, 1, i)
table.cell(panel, 0, i, row.value, height = row.kind == 5 ? (anchor == "Top Left" ? (vBoardMode == "Audit" ? math.min(vHeaderClearance, 8) : vHeaderClearance) : 5) : 0, text_color = row.kind == 3 ? vMuted : row.kind == 2 or row.kind == 4 ? vAccent : vText, bgcolor = background, text_size = row.kind == 2 ? vTitleSize : row.kind == 3 ? vSmallSize : vBodySize, text_halign = text.align_left, text_font_family = font.family_default, text_formatting = row.kind == 2 or row.kind == 4 ? text.format_bold : text.format_none, tooltip = row.tip)
// Setter pools survive ordinary price/text changes; surplus handles are deleted.
f_vLine(array<line> pool, int slot, int x1, float y1, int x2, float y2, color tint, string style, int width = 1) =>
line item = na
if slot < array.size(pool)
item := array.get(pool, slot)
line.set_xy1(item, x1, y1)
line.set_xy2(item, x2, y2)
line.set_color(item, tint)
line.set_style(item, style)
line.set_width(item, width)
else
item := line.new(x1, y1, x2, y2, xloc = xloc.bar_time, color = tint, style = style, width = width)
array.push(pool, item)
item
f_vLabel(array<label> pool, int slot, int stamp, float price, string value, color tint, string tip, bool faceLeft = false, bool solid = false) =>
label item = na
if slot < array.size(pool)
item := array.get(pool, slot)
label.set_xy(item, stamp, price)
label.set_text(item, value)
label.set_color(item, solid ? tint : vBg)
label.set_textcolor(item, solid ? f_vInk(tint) : tint)
label.set_size(item, vTagSize)
label.set_style(item, faceLeft ? label.style_label_right : label.style_label_left)
label.set_tooltip(item, tip)
else
item := label.new(stamp, price, value, xloc = xloc.bar_time, style = faceLeft ? label.style_label_right : label.style_label_left, color = solid ? tint : vBg, textcolor = solid ? f_vInk(tint) : tint, size = vTagSize, text_font_family = font.family_default, tooltip = tip)
array.push(pool, item)
item
f_vMarker(array<label> pool, int slot, int stamp, float price, string value, color tint, string tip, string style) =>
label item = f_vLabel(pool, slot, stamp, price, value, tint, tip, false, true)
label.set_style(item, style)
item
f_vBox(array<box> pool, int slot, int left, float top, int right, float bottom, color tint) =>
box item = na
if slot < array.size(pool)
item := array.get(pool, slot)
box.set_lefttop(item, left, top)
box.set_rightbottom(item, right, bottom)
box.set_bgcolor(item, tint)
box.set_border_color(item, color.new(tint, 100))
else
item := box.new(left, top, right, bottom, xloc = xloc.bar_time, bgcolor = tint, border_color = color.new(tint, 100))
array.push(pool, item)
item
f_vTagY(array<float> used, float price, float gap) =>
float result = price
for attempt = 0 to 24
float candidate = attempt == 0 ? price : price + (attempt % 2 == 1 ? 1 : -1) * math.ceil(attempt / 2.0) * gap
bool clear = true
for previous in used
if math.abs(candidate - previous) < gap * 0.95
clear := false
if clear
result := candidate
break
array.push(used, result)
result
f_vExtent(array<float> bounds, float price) =>
if not na(price)
array.set(bounds, 0, math.min(array.get(bounds, 0), price))
array.set(bounds, 1, math.max(array.get(bounds, 1), price))
true
f_vTagGap(float renderedRange, float tick, int fontPixels) =>
// Bounded display heuristic: map/history lines also expand autoscale. Pine
// exposes no pane pixel height, so this is not a universal collision proof.
float fraction = math.min(0.07, math.max(0.035, fontPixels * 0.004))
math.max(renderedRange * fraction, tick * 4)
float vFallbackRange = ta.highest(high[1], 80) - ta.lowest(low[1], 80)
var float vVisibleHigh = na
var float vVisibleLow = na
if time == chart.left_visible_bar_time
vVisibleHigh := high
vVisibleLow := low
else if time > chart.left_visible_bar_time and time <= chart.right_visible_bar_time
vVisibleHigh := math.max(nz(vVisibleHigh, high), high)
vVisibleLow := math.min(nz(vVisibleLow, low), low)
var array<line> vLines = array.new<line>()
var array<label> vLabels = array.new<label>()
var array<label> vMarkers = array.new<label>()
var array<box> vBoxes = array.new<box>()
var table vBoard = na
var table vFocus = na
var string vBoardSignature = ""
var string vFocusSignature = ""
// VIEW RENDER — exact fixture insertion boundary; no production markers precede it.
if (barstate.islastconfirmedhistory or barstate.isrealtime) and (vBoardMode != "Off" or vDetail != "Off" or vChartMode != "Off")
int vTrackIndex = f_vTrackedIndex()
bool vTracking = vTrackIndex >= 0
CamPlanSnapshot vTracked = vTracking ? array.get(camPlans, vTrackIndex) : CamPlanSnapshot.new()
int vNextIndex = f_vFallbackFocus()
Play vUpcoming = vNextIndex >= 0 ? array.get(plays, vNextIndex) : Play.new()
int vInspectIndex = vFocusCode == "Auto" ? vNextIndex : f_camCodeIndex(vFocusCode)
bool vHasUpcoming = vNextIndex >= 0 and not vTracking
string vSourceTip = f_vSourceTip()
string vRiskTip = f_vRiskTip()
string vClosedTip = "Last committed chart close, UTC: " + f_vTime(camClosedTime) + "\nPrice: " + f_camExact(camClosedPrice) + "\nForming quotes do not update this value."
int vBarMs = int(timeframe.in_seconds() * 1000)
int vNow = na(camClosedTime) ? time : camClosedTime
int vRight = vNow + vBarMs * 4
int vLeft = nz(chart.left_visible_bar_time, vNow - vBarMs * 60)
int vVisibleRight = math.min(vNow, nz(chart.right_visible_bar_time, vNow))
bool vLeftPanel = array.includes(vUsedAnchors, "Top Left") or array.includes(vUsedAnchors, "Bottom Left") or array.includes(vUsedAnchors, "Middle Left")
int vTagTime = vTagSide == "Right" ? vRight : vLeft + int(math.max(0, vVisibleRight - vLeft) * (vLeftPanel ? 0.45 : 0.12))
float vRange = math.max(nz(vVisibleHigh - vVisibleLow, nz(vFallbackRange, syminfo.mintick * 100)), syminfo.mintick * 20)
int vLineCount = 0
int vLabelCount = 0
int vMarkerCount = 0
int vBoxCount = 0
int vHistoryShown = 0
int vHistoryOmitted = 0
int vContextOmitted = 0
array<float> vOccupied = array.new<float>()
bool vCanMap = camChartSupported and camMapReady and camMap.valid and not na(camSessionStart) and vChartMode != "Off"
float vFocusLevel = vTracking ? vTracked.triggerPrice : vHasUpcoming ? vUpcoming.triggerPrice : na
int vRoleDirection = vTracking ? vTracked.dir : vHasUpcoming ? vUpcoming.dir : 0
// Admit before drawing. Candle range still controls the existing history
// proximity filter; the wider rendered extent controls label spacing only.
array<float> vExtent = array.from(nz(vVisibleLow, nz(camClosedPrice, close) - vRange * 0.5), nz(vVisibleHigh, nz(camClosedPrice, close) + vRange * 0.5))
array<float> vMapLevels = array.from(camMap.l6, camMap.l4, camMap.l3, camMap.c, camMap.h3, camMap.h4, camMap.h6)
array<bool> vMapVisible = array.new_bool(7, false)
array<int> vHistoryIndices = array.new_int()
array<int> vAuditIndices = array.new_int()
int vReservedTags = 0
if vChartMode != "Off" and vTracking
for price in array.from(vTracked.entry, vTracked.stop, vTracked.target)
f_vExtent(vExtent, price)
vReservedTags := vPriceTags ? 3 : 0
if vCanMap
for pass = 0 to 1
for j = 0 to 6
float price = array.get(vMapLevels, j)
bool focused = not na(vFocusLevel) and f_camCompare(price, vFocusLevel) == 0
bool visible = not na(price) and (vChartMode != "Clean" or focused or (not vTracking and not vHasUpcoming and j == 3))
if visible and ((pass == 0 and focused) or (pass == 1 and not focused))
array.set(vMapVisible, j, true)
f_vExtent(vExtent, price)
if vPriceTags and vReservedTags < vTagCap
vReservedTags += 1
if vMiddleBand and (vChartMode == "Context" or vChartMode == "Audit")
f_vExtent(vExtent, camMap.l2)
f_vExtent(vExtent, camMap.h2)
if vChartMode != "Off" and array.size(camPlans) > 0
for i = 0 to array.size(camPlans) - 1
CamPlanSnapshot plan = array.get(camPlans, i)
if plan.state != "ACTIVE" and not na(plan.endTime) and not na(plan.activationTime)
bool timeVisible = plan.endTime >= vLeft and plan.activationTime <= vVisibleRight
bool priceNear = math.max(plan.entry, math.max(plan.stop, plan.target)) <= nz(vVisibleHigh, plan.entry) + vRange and math.min(plan.entry, math.min(plan.stop, plan.target)) >= nz(vVisibleLow, plan.entry) - vRange
if timeVisible and priceNear and array.size(vHistoryIndices) < vHistory
array.push(vHistoryIndices, i)
for price in array.from(plan.entry, plan.stop, plan.target)
f_vExtent(vExtent, price)
if vEpisodeMarks
f_vExtent(vExtent, nz(plan.outcomePrice, plan.entry))
if vPriceTags and not vEpisodeMarks and vReservedTags < vTagCap
vReservedTags += 1
else
vHistoryOmitted += 1
if vChartMode == "Audit" and vPriceTags and array.size(camEvents) > 0
for i = 0 to math.min(3, array.size(camEvents) - 1)
CamEvent event = array.get(camEvents, i)
if vReservedTags < vTagCap and event.eventTime >= vLeft and event.eventTime <= vVisibleRight and not na(event.price)
array.push(vAuditIndices, i)
f_vExtent(vExtent, event.price)
vReservedTags += 1
float vRenderedRange = math.max(array.get(vExtent, 1) - array.get(vExtent, 0), syminfo.mintick * 20)
float vGap = f_vTagGap(vRenderedRange, syminfo.mintick, vTagSize)
// Reserve marker text rows before right/left-column price tags. A local
// ending can share H4/L3 exactly; displace only the tag with its true leader.
if vChartMode != "Off" and vEpisodeMarks
if vTracking and vTracked.activationTime >= vLeft
array.push(vOccupied, vTracked.entry + (vTracked.dir == 1 ? -0.6 : 0.6) * vGap)
for i in vHistoryIndices
CamPlanSnapshot plan = array.get(camPlans, i)
if plan.activationTime >= vLeft
array.push(vOccupied, plan.entry + (plan.dir == 1 ? -0.6 : 0.6) * vGap)
array.push(vOccupied, nz(plan.outcomePrice, plan.state == "STOP" ? plan.stop : plan.state == "TARGET" ? plan.target : plan.entry))
// Manual vInspectIndex cannot replace this immutable tracked snapshot.
if vChartMode != "Off" and vTracking
array<float> prices = array.from(vTracked.entry, vTracked.stop, vTracked.target)
array<string> names = array.from("PLAN ENTRY REF", "PLAN STOP", "PLAN TARGET")
array<color> colors = array.from(vAccent, vDown, vUp)
for j = 0 to 2
float price = array.get(prices, j)
color tint = array.get(colors, j)
f_vLine(vLines, vLineCount, vTracked.activationTime, price, vRight, price, tint, line.style_solid, j == 0 ? 2 : 1)
vLineCount += 1
if vTagTime < vTracked.activationTime
f_vLine(vLines, vLineCount, vTagTime, price, vTracked.activationTime, price, color.new(tint, 45), line.style_dotted)
vLineCount += 1
if vPriceTags and vLabelCount < vTagCap
float tagY = f_vTagY(vOccupied, price, vGap)
string tip = "Tracked " + vTracked.code + " · episode " + str.tostring(vTracked.episode) + "\n" + array.get(names, j) + " " + f_camExact(price) + "\nFrozen at " + f_vTime(vTracked.activationTime) + " UTC. Dotted left extension is a present reference guide, not historical availability."
f_vLabel(vLabels, vLabelCount, vTagTime, tagY, array.get(names, j) + " " + f_camPrice(price), tint, tip, vTagSide == "Right", vTagSide == "Right")
vLabelCount += 1
if tagY != price
f_vLine(vLines, vLineCount, vTagTime, price, vTagTime, tagY, color.new(tint, 40), line.style_dotted)
vLineCount += 1
if vPlanZones
f_vBox(vBoxes, vBoxCount, vTracked.activationTime, math.max(vTracked.entry, vTracked.stop), vRight, math.min(vTracked.entry, vTracked.stop), color.new(vDown, 100 - vZoneOpacity))
vBoxCount += 1
f_vBox(vBoxes, vBoxCount, vTracked.activationTime, math.max(vTracked.entry, vTracked.target), vRight, math.min(vTracked.entry, vTracked.target), color.new(vUp, 100 - vZoneOpacity))
vBoxCount += 1
if vEpisodeMarks and vTracked.activationTime >= vLeft and vTracked.activationTime <= vVisibleRight
string tip = "REFERENCE ENTRY · " + vTracked.code + " · episode " + str.tostring(vTracked.episode) + "\nConfirmed " + f_vTime(vTracked.activationTime) + " UTC @ " + f_camExact(vTracked.entry) + "\nFrozen stop / target " + f_camExact(vTracked.stop) + " / " + f_camExact(vTracked.target) + "\n" + vTracked.reason + "\nNot a broker fill or a fresh entry at the latest quote."
f_vMarker(vMarkers, vMarkerCount, vTracked.activationTime, vTracked.entry, vTracked.code + (vTracked.dir == 1 ? " ▲ ENTRY" : " ▼ ENTRY"), vTracked.dir == 1 ? vUp : vDown, tip, vTracked.dir == 1 ? label.style_label_up : label.style_label_down)
vMarkerCount += 1
// Role color belongs to the relevant play, never the current quote's position.
if vCanMap
array<string> codes = array.from("L6", "L4", "L3", "PC", "H3", "H4", "H6")
for pass = 0 to 1
for j = 0 to 6
float price = array.get(vMapLevels, j)
bool focused = not na(vFocusLevel) and f_camCompare(price, vFocusLevel) == 0
bool visible = array.get(vMapVisible, j)
if visible and ((pass == 0 and focused) or (pass == 1 and not focused))
color tint = focused and vRoleDirection != 0 ? (vRoleDirection == 1 ? vUp : vDown) : j < 3 ? vUp : j > 3 ? vDown : vAccent
f_vLine(vLines, vLineCount, camSessionStart, price, vRight, price, color.new(tint, focused ? 0 : vChartMode == "Map" ? 20 : 50), vChartMode == "Map" or focused ? line.style_solid : line.style_dotted, focused or j == 2 or j == 4 ? 2 : 1)
vLineCount += 1
if vPriceTags
if vLabelCount < vTagCap
float tagY = f_vTagY(vOccupied, price, vGap)
string tip = "CURRENT MAP · " + array.get(codes, j) + " " + f_camExact(price) + "\nMap usable at decision opening " + f_vTime(camSessionStart) + " UTC. A dotted left guide is a current reference only.\n" + vSourceTip
f_vLabel(vLabels, vLabelCount, vTagTime, tagY, (focused ? "TRIGGER " : "") + array.get(codes, j) + " " + f_camPrice(price), tint, tip, vTagSide == "Right", vTagSide == "Right")
vLabelCount += 1
if vTagTime < camSessionStart
f_vLine(vLines, vLineCount, vTagTime, price, camSessionStart, price, color.new(tint, 70), line.style_dotted)
vLineCount += 1
if tagY != price
f_vLine(vLines, vLineCount, vTagTime, price, vTagTime, tagY, color.new(tint, 45), line.style_dotted)
vLineCount += 1
else
vContextOmitted += 1
if vMiddleBand and (vChartMode == "Context" or vChartMode == "Audit")
f_vBox(vBoxes, vBoxCount, camSessionStart, camMap.h2, vRight, camMap.l2, color.new(vAccent, 96))
vBoxCount += 1
// Ended geometry uses its own snapshot and stops exactly at its endTime.
if array.size(vHistoryIndices) > 0
for i in vHistoryIndices
CamPlanSnapshot plan = array.get(camPlans, i)
if plan.state != "ACTIVE"
if vHistoryShown < vHistory
vHistoryShown += 1
array<float> prices = array.from(plan.entry, plan.stop, plan.target)
for price in prices
color endedTint = price == plan.stop ? vDown : price == plan.target ? vUp : vAccent
f_vLine(vLines, vLineCount, plan.activationTime, price, plan.endTime, price, color.new(endedTint, 35), line.style_dashed)
vLineCount += 1
if vPlanZones
f_vBox(vBoxes, vBoxCount, plan.activationTime, math.max(plan.entry, plan.stop), plan.endTime, math.min(plan.entry, plan.stop), color.new(vDown, 100 - math.max(4, vZoneOpacity - 4)))
vBoxCount += 1
f_vBox(vBoxes, vBoxCount, plan.activationTime, math.max(plan.entry, plan.target), plan.endTime, math.min(plan.entry, plan.target), color.new(vUp, 100 - math.max(4, vZoneOpacity - 4)))
vBoxCount += 1
if vEpisodeMarks
string markerTip = "REFERENCE EPISODE · " + plan.code + " #" + str.tostring(plan.episode) + "\n" + f_vTime(plan.activationTime) + " → " + f_vTime(plan.endTime) + " UTC\nFrozen entry / stop / target " + f_camExact(plan.entry) + " / " + f_camExact(plan.stop) + " / " + f_camExact(plan.target) + "\n" + plan.reason + (plan.ambiguous ? "\nAmbiguous OHLC path; stop-first reference convention." : "") + "\nThese are observations, not broker fills."
if plan.activationTime >= vLeft
f_vMarker(vMarkers, vMarkerCount, plan.activationTime, plan.entry, plan.code + (plan.dir == 1 ? " ▲ ENTRY" : " ▼ ENTRY"), plan.dir == 1 ? vUp : vDown, markerTip, plan.dir == 1 ? label.style_label_up : label.style_label_down)
vMarkerCount += 1
float markerPrice = nz(plan.outcomePrice, plan.state == "STOP" ? plan.stop : plan.state == "TARGET" ? plan.target : plan.entry)
string ending = plan.code + (plan.state == "TARGET" ? " ✓ " : plan.state == "STOP" ? " × " : " · ") + plan.state + (plan.ambiguous ? " ?" : "")
string endingTip = na(plan.outcomePrice) ? "Unpriced ending; marker anchored at frozen reference " + f_camExact(markerPrice) + " for orientation. No exit price or fill inferred." : "Observed end reference " + f_camExact(markerPrice)
f_vMarker(vMarkers, vMarkerCount, plan.endTime, markerPrice, ending, plan.state == "TARGET" ? vUp : plan.state == "STOP" ? vDown : vMuted, markerTip + "\n" + endingTip, label.style_label_left)
vMarkerCount += 1
if vPriceTags and not vEpisodeMarks and vLabelCount < vTagCap
float tagY = f_vTagY(vOccupied, plan.entry, vGap)
string tip = "ENDED " + plan.code + " · episode " + str.tostring(plan.episode) + "\n" + f_vTime(plan.activationTime) + " → " + f_vTime(plan.endTime) + " UTC\nFrozen entry / stop / target: " + f_camExact(plan.entry) + " / " + f_camExact(plan.stop) + " / " + f_camExact(plan.target) + "\n" + plan.reason + (plan.ambiguous ? "\nSame-bar ambiguity: stop-first reference policy." : "")
f_vLabel(vLabels, vLabelCount, plan.endTime, tagY, "ENDED " + f_vDirection(plan.dir) + " · " + plan.state, vMuted, tip, true)
vLabelCount += 1
if tagY != plan.entry
f_vLine(vLines, vLineCount, plan.endTime, plan.entry, plan.endTime, tagY, color.new(vMuted, 65), line.style_dotted)
vLineCount += 1
if array.size(vAuditIndices) > 0
for i in vAuditIndices
CamEvent event = array.get(camEvents, i)
if vLabelCount < vTagCap and event.eventTime >= vLeft and event.eventTime <= vVisibleRight and not na(event.price)
float tagY = f_vTagY(vOccupied, event.price, vGap)
f_vLabel(vLabels, vLabelCount, event.eventTime, tagY, event.code + " " + event.kind, vMuted, event.reason + "\nObserved " + f_vTime(event.eventTime) + " UTC · not a broker fill", true)
vLabelCount += 1
if tagY != event.price
f_vLine(vLines, vLineCount, event.eventTime, event.price, event.eventTime, tagY, color.new(vMuted, 65), line.style_dotted)
vLineCount += 1
while array.size(vLines) > vLineCount
line.delete(array.pop(vLines))
while array.size(vLabels) > vLabelCount
label.delete(array.pop(vLabels))
while array.size(vMarkers) > vMarkerCount
label.delete(array.pop(vMarkers))
while array.size(vBoxes) > vBoxCount
box.delete(array.pop(vBoxes))
if vBoardMode != "Off"
array<CamViewRow> rows = array.new<CamViewRow>()
f_vInsetStart(rows, vBoardAnchor)
string vRibbonContext = f_vSourceShort(camSourceLabel) + " · " + (not camMapReady ? "CONTEXT WAIT" : camBias == 1 ? "HIGHER" : camBias == -1 ? "LOWER" : "NEUTRAL") + (camMapReady ? " · " + str.upper(camWidthLabel) : "")
string vAtrShort = str.contains(camAtrBasis, "RTH") ? "RTH14" : str.contains(camAtrBasis, "Provider") ? "Daily14" : "WAIT"
string vAtrValue = camAtrReady ? f_camPrice(camAtrReference) : str.tostring(camAtrSamples) + "/14 WAIT"
string vAtrTip = "ATR basis: " + camAtrBasis + "\nFrozen value " + f_camExact(camAtrReference) + " at " + f_vTime(camAtrStamp) + " UTC\nObserved chain start " + f_vTime(camAtrStart) + " · samples " + str.tostring(camAtrSamples) + "\nRTH counts completed observed sessions; entirely absent dates may be closures or unavailable feed. No provider fallback.\n" + vRiskTip
if vBoardMode == "Ribbon"
f_vAdd(rows, "", vRibbonContext, "CAMARILLA DESK " + CAM_VERSION + " · " + syminfo.tickerid + "\n" + vSourceTip, 4)
string status = vTracking ? vTracked.code + " " + f_vDirection(vTracked.dir) + " · ACTIVE" + (camActiveCount > 1 ? " +" + str.tostring(camActiveCount - 1) : "") : vHasUpcoming ? vUpcoming.code + " " + f_vDirection(vUpcoming.dir) + " · " + f_vState(vUpcoming, camInSession) : not camMapReady ? "DATA WAIT" : not camInSession ? "WAIT SESSION" : "NO QUALIFYING SETUP"
string statusTip = vTracking ? f_vSnapshotName(vTracked) + "\nConfirmed " + f_vTime(vTracked.activationTime) + " UTC\nEntry / stop / target " + f_camExact(vTracked.entry) + " / " + f_camExact(vTracked.stop) + " / " + f_camExact(vTracked.target) + "\n" + vTracked.reason : vHasUpcoming ? f_vPlayTip(vUpcoming) : f_vRecovery()
f_vAdd(rows, "", status, statusTip + "\n" + str.tostring(camActiveCount) + " active · " + str.tostring(camApproachingCount) + " approaching · " + str.tostring(camCandidateCount) + " candidates. These counts overlap. Expand Clean/Context/Audit for detail.", 2)
string next = vTracking ? "NEXT · Stop / target · " + str.tostring(math.max(0, activeDeadline - (camClosedBar - vTracked.activationBar))) + "b max" : vHasUpcoming ? vUpcoming.state == "ARMED" ? "NEXT · " + f_vTrigger(vUpcoming) : vUpcoming.approaching ? "NEXT · Wait for setup" : "NEXT · Wait for approach" : not camMapReady ? "NEXT · Check source / ATR readiness" : not camInSession ? "NEXT · Wait for " + f_vClockShort(camDecisionLabel) : "NEXT · Inspect Audit for prerequisites"
f_vAdd(rows, "", next, statusTip + "\n" + (vTracking ? "Session end can precede the displayed bar deadline. References are not broker fills." : vHasUpcoming ? vUpcoming.next : f_vRecovery()), 3)
f_vAdd(rows, "", "ATR " + vAtrShort + " " + vAtrValue + " · CLOSED " + f_vClock(camClosedTime), vAtrTip + "\n" + vClosedTip, 3)
else
f_vAdd(rows, "", "CAMARILLA DESK " + CAM_VERSION + " · " + syminfo.ticker, syminfo.tickerid + " · " + timeframe.period + "\n" + vSourceTip, 3)
if vTracking
f_vAdd(rows, "", f_vWrap(f_vDirection(vTracked.dir) + " · " + f_vSnapshotName(vTracked), 31, 2), vTracked.reason, 2)
int age = math.max(0, camClosedBar - vTracked.activationBar)
f_vAdd(rows, "", "ACTIVE · " + str.tostring(age) + " bars · confirmed " + f_vClock(vTracked.activationTime) + " UTC", "Frozen activation " + f_vTime(vTracked.activationTime) + " UTC · episode " + str.tostring(vTracked.episode), 4)
f_vAdd(rows, "ENTRY REF", f_camPrice(vTracked.entry), "Observed activation close, not a fill.\n" + f_camExact(vTracked.entry), 1)
f_vAdd(rows, "STOP", f_camPrice(vTracked.stop), "Frozen " + vTracked.stopMode + " stop\n" + f_camExact(vTracked.stop), 1)
f_vAdd(rows, "TARGET", f_camPrice(vTracked.target) + " · " + str.tostring(vTracked.effectiveR, "0.00") + "R", "Frozen " + vTracked.targetMode + " target\n" + f_camExact(vTracked.target), 1)
f_vAdd(rows, "LAST CLOSED", f_camPrice(camClosedPrice) + " · " + f_vClock(camClosedTime), vClosedTip, 1)
f_vAdd(rows, "", "NEXT · stop / target / expiry · " + str.tostring(math.max(0, activeDeadline - age)) + " bars left", "Later completed-bar observations resolve the fixed plan. Session expiry can precede the bar deadline.\n" + vRiskTip, 3)
else if vHasUpcoming
f_vAdd(rows, "", f_vWrap(f_vDirection(vUpcoming.dir) + " · " + f_vName(vUpcoming), 31, 2), f_vPlayTip(vUpcoming), 2)
f_vAdd(rows, "", f_vState(vUpcoming, camInSession) + (f_vAge(vUpcoming) == "" ? "" : " · " + f_vAge(vUpcoming)), f_vPlayTip(vUpcoming), 4)
f_vAdd(rows, "", "CONFIRM · " + f_vTrigger(vUpcoming), "This close also requires the preceding setup sequence.\n" + vUpcoming.next, 0)
f_vAdd(rows, "", "NEXT · " + f_vWrap(vUpcoming.next, 36, 2), vUpcoming.next, 3)
f_vAdd(rows, "LAST CLOSED", f_camPrice(camClosedPrice) + " · " + f_vClock(camClosedTime), vClosedTip, 1)
else
f_vAdd(rows, "", "SESSION GUIDE", "No active plan or eligible setup is selected.", 2)
f_vAdd(rows, "", not camMapReady ? "DATA WAIT" : not camInSession ? "WAIT SESSION" : "NO QUALIFYING SETUP", camDataReason, 4)
f_vAdd(rows, "", f_vWrap(f_vRecovery(), 38, 3), f_vRecovery(), 0)
f_vAdd(rows, "LAST CLOSED", f_camPrice(camClosedPrice) + " · " + f_vClock(camClosedTime), vClosedTip, 1)
if array.size(camPlans) > 0
CamPlanSnapshot last = array.get(camPlans, 0)
if last.state != "ACTIVE"
f_vAdd(rows, "", f_vWrap("LAST ENDED · " + f_vDirection(last.dir) + " " + f_vSnapshotName(last) + " · " + last.state, 38, 2), last.reason + "\nEnd " + f_vTime(last.endTime) + " UTC; not a new setup.", 3)
int otherCount = math.max(0, camActiveCount - (vTracking ? 1 : 0))
if otherCount > 0
string others = ""
int shown = 0
for plan in camPlans
if plan.state == "ACTIVE" and (not vTracking or plan.code != vTracked.code or plan.episode != vTracked.episode or plan.sessionStart != vTracked.sessionStart)
if shown < 2
others += (shown == 0 ? "" : " · ") + f_vDirection(plan.dir) + " " + f_vSnapshotName(plan)
shown += 1
f_vAdd(rows, "", f_vWrap("ALSO ACTIVE · " + others + (otherCount > shown ? " +" + str.tostring(otherCount - shown) : ""), 38, 2), str.tostring(otherCount) + " additional active snapshots. Only the newest active is chart-tracked. Manual inspection cannot replace it.", 3)
f_vAdd(rows, "", str.tostring(camActiveCount) + " active · " + str.tostring(camApproachingCount) + " approaching · " + str.tostring(camCandidateCount) + (camCandidateCount == 1 ? " candidate" : " candidates"), "Approaching is a candidate whose region prerequisite passes. Counts overlap; they are not probabilities.", 3)
if vBoardMode == "Context" or vBoardMode == "Audit"
array<int> attention = f_vRows(vBoardMode == "Audit")
f_vAdd(rows, "", vBoardMode == "Audit" ? "ALL 14 · PREREQUISITE AUDIT" : "ATTENTION · UP TO FIVE PLAYS", "ACTIVE → ARMED → APPROACHING → CANDIDATE; fixed-code ties. Attention, not strength.", 4)
for i in attention
Play play = array.get(plays, i)
string state = f_vState(play, camInSession)
string brief = vBoardMode == "Audit" ? play.code + " · " + f_vDirection(play.dir) + " · " + state : f_vName(play) + " · " + f_vDirection(play.dir) + "\n" + state + (play.state == "ARMED" or play.state == "WATCH" and play.candidate ? " · " + f_vTrigger(play) : "")
f_vAdd(rows, "", brief, f_vName(play) + "\n" + f_vPlayTip(play) + "\n" + f_vGateWords(play), 3)
f_vAdd(rows, "", vRibbonContext, vSourceTip, 4)
f_vAdd(rows, "", "ATR " + vAtrShort + " " + vAtrValue + " · " + f_vClockShort(camDecisionLabel), vAtrTip, 3)
f_vAdd(rows, "", "MIN " + str.tostring(minRewardR, "0.0") + "R · " + str.tostring(stopBufferTicks) + " tick buffer · " + stopModel + " SL", vRiskTip, 3)
if vBoardMode == "Audit"
f_vAdd(rows, "", "CONTEXT " + (camBias == 1 ? "Higher" : camBias == -1 ? "Lower" : "Neutral") + " · WIDTH " + camWidthLabel, "Close delta " + f_camExact(camRangeDelta) + " · neutral threshold " + f_camExact(camRangeThreshold) + "\nGeometric band relation (separate): " + camRelationship + "\nWidth ratio " + f_camExact(camWidthRatio) + "\nSelected ATR " + f_camExact(camAtrReference) + " at " + f_vTime(camAtrStamp) + " UTC", 3)
f_vAdd(rows, "", "REARM " + (allowRearm ? "ON · " + str.tostring(cooldownBars) + "b · cap " + str.tostring(maxEpisodes) : "OFF") + " · TP " + targetModel, vRiskTip, 3)
f_vAdd(rows, "", "REFERENCE PLANS · NOT ORDER FILLS", "Drawings: " + str.tostring(vHistoryShown) + " ended shown / " + str.tostring(vHistoryOmitted) + " omitted; " + str.tostring(vContextOmitted) + " context tags omitted.\n" + (CAM_IS_LAB ? "Lab: alert() calls for reference JSON; order-fill alerts for emulated orders. " : "Static activation alert: desired condition + Once Per Bar Close. ") + "Aggregate JSON: Any alert() function call. Recreate saved alerts after changes. Displaying this recipe does not test delivery.", 3)
f_vInsetEnd(rows, vBoardAnchor)
string signature = f_vSignature(rows)
if na(vBoard) or signature != vBoardSignature
if not na(vBoard)
table.delete(vBoard)
vBoard := table.new(f_vPosition(vBoardAnchor), 2, array.size(rows), bgcolor = color.new(vBg, 100), frame_width = 0, border_width = 0)
vBoardSignature := signature
f_vRender(vBoard, rows, vBoardAnchor)
if vDetail != "Off"
array<CamViewRow> rows = array.new<CamViewRow>()
f_vInsetStart(rows, vFocusAnchor)
if vInspectIndex >= 0 and vInspectIndex < array.size(plays)
Play play = array.get(plays, vInspectIndex)
string tip = f_vPlayTip(play)
f_vAdd(rows, "", "INSPECT · " + f_vName(play), tip, 4)
f_vAdd(rows, "", f_vDirection(play.dir) + " · " + f_vState(play, camInSession) + (f_vAge(play) == "" ? "" : " · " + f_vAge(play)), tip, 2)
f_vAdd(rows, "", "WHY · " + f_vWrap(play.reason, 38, 3), play.reason, 0)
f_vAdd(rows, "", "NEXT · " + f_vWrap(play.next, 38, 3), play.next, 0)
f_vAdd(rows, "", "FAIL · " + f_vWrap(play.invalidation, 38, 2), play.invalidation, 3)
if vDetail == "Standard"
f_vAdd(rows, "", f_vGateWords(play), "Direct core flags. Candidate excludes current region; Approaching includes it. SMA is an optional original close filter.", 3)
f_vAdd(rows, "", "SETUP " + f_vTime(play.setupTime) + " · ACTIVE " + f_vTime(play.activationTime), tip, 3)
f_vAdd(rows, "", "OPEN " + f_camPrice(camOpen) + " · " + f_vTime(camOpeningSourceTime) + " UTC", vSourceTip, 3)
f_vAdd(rows, "", f_vWrap("SOURCE " + camSourceLabel + " · close " + camCloseLabel + " · " + camDecisionLabel, 40, 2), vSourceTip, 3)
f_vAdd(rows, "", "ORIGINAL EXECUTION\n" + stopModel + " SL / " + targetModel + " TP · min " + str.tostring(minRewardR) + "R", vRiskTip, 3)
f_vAdd(rows, "", CAM_IS_LAB ? "ALERT · alert() calls = reference JSON\nOrder fills = broker-emulated orders" : "ALERT · activation condition + bar close\nJSON · Any alert() function call", "Choose the desired condition; recreate after input/code changes. Reference plans are not fills. No alert delivery is proven by this display.", 3)
else
f_vAdd(rows, "", "INSPECT · SESSION GUIDE", "Choose a code to audit its prerequisites. Chart tracking remains independent.", 4)
f_vAdd(rows, "", f_vWrap(f_vRecovery(), 38, 3), vSourceTip, 0)
f_vAdd(rows, "", vTracking ? f_vWrap("CHART TRACKS · " + f_vDirection(vTracked.dir) + " " + f_vSnapshotName(vTracked), 38, 2) : "CHART · no active plan", "Manual inspection cannot change tracked snapshot selection.", 3)
f_vInsetEnd(rows, vFocusAnchor)
string signature = f_vSignature(rows)
if na(vFocus) or signature != vFocusSignature
if not na(vFocus)
table.delete(vFocus)
vFocus := table.new(f_vPosition(vFocusAnchor), 2, array.size(rows), bgcolor = color.new(vBg, 100), frame_width = 0, border_width = 0)
vFocusSignature := signature
f_vRender(vFocus, rows, vFocusAnchor)
// STRATEGY LAB · broker-emulated experiment, separate from structural observations.
string labGroup = "Strategy Lab · one position · next available open"
string labCode = input.string("All · fixed code order", "Eligible signal selection", options = ["All · fixed code order", "HA", "LA", "HB", "LB", "HC", "LC", "HD", "LD", "HE", "LE", "HF", "LF", "HG", "LG"], group = labGroup, display = display.none, tooltip = "When several ACTIVE events share a close, the first permitted code in HA, LA, HB, LB … HG, LG order wins. This is a deterministic selection rule, not a ranking of signal quality.")
string labSide = input.string("Both", "Direction", options = ["Both", "Long only", "Short only"], group = labGroup, display = display.none)
float labRiskPct = input.float(0.5, "Risk budget · % equity at signal", minval = 0.01, maxval = 5, step = 0.05, group = labGroup, display = display.none)
float labExposurePct = input.float(90, "Maximum notional · % equity", minval = 1, maxval = 100, step = 5, group = labGroup, display = display.none, tooltip = "Default 90% reserves headroom for fees and price movement; 100% remains available for explicit stress tests. This cannot prevent every margin call. Uses symbol point value and account-currency conversion. Risk is estimated at signal price; gaps can exceed it.")
float labQtyStep = input.float(0, "Quantity increment · 0 uses symbol minimum", minval = 0, group = labGroup, display = display.none, tooltip = "Uses syminfo.mincontract when zero. Verify your instrument and broker contract specification. No quantity is rounded up to force a trade.")
int labFrom = input.time(timestamp("01 Jan 2020 00:00 +0000"), "First signal time", group = labGroup, display = display.none)
int labTo = input.time(timestamp("01 Jan 2099 00:00 +0000"), "Last signal time", group = labGroup, display = display.none)
bool labShow = input.bool(true, "Show small Lab readout", group = labGroup, display = display.none)
f_labQuantity(float equity, float budgetPct, float capPct, float riskPerUnit, float valuePerUnit, float step) =>
bool valid = f_camSign(equity) > 0 and f_camSign(riskPerUnit) > 0 and f_camSign(valuePerUnit) > 0 and f_camSign(step) > 0 and f_camSign(budgetPct) > 0 and f_camSign(capPct) > 0
float steps = valid ? math.min(equity * budgetPct / 100 / riskPerUnit, equity * capPct / 100 / valuePerUnit) / step : 0.0
// Normalize only floating representation noise at a whole-step boundary.
float nearest = math.round(steps)
float stepTolerance = math.min(1e-5, 4.0 * 2.220446049250313e-16 * math.max(1, math.abs(steps)))
float normalized = f_camSign(math.abs(steps - nearest) - stepTolerance) <= 0 ? nearest : steps
valid ? math.floor(normalized) * step : 0.0
var string labTrackedCode = ""
var int labTrackedSession = na
var int labTrackedEpisode = na
var int labSignalBar = na
var int labExpiryBar = na
var int labSessionEnd = na
var int labLastExitBar = na
var int labPreviousClosed = 0
var float labStop = na
var float labTarget = na
var int labSignals = 0
var int labSkipped = 0
var int labMarginExits = 0
var int labFirstReady = na
var int labLastReady = na
var int labReadyBars = 0
var string labStatus = "Waiting for a confirmed ACTIVE event"
var table labPanel = na
if barstate.isfirst and labShow
string labAnchor = f_vFree(vUsedAnchors, "Bottom Right")
labPanel := table.new(f_vPosition(labAnchor), 1, 5, bgcolor = vBg, frame_color = vFrame, frame_width = 1)
if barstate.isfirst and labTo <= labFrom
runtime.error("Strategy Lab: last signal time must be after first signal time.")
if barstate.isconfirmed
if camMapReady and camOpenKnown and camInSession and camBarOhlcValid and time_close >= labFrom and time_close < labTo
labFirstReady := na(labFirstReady) ? time_close : labFirstReady
labLastReady := time_close
labReadyBars += 1
// Closed-trade records include partial margin liquidations. A surviving
// position must keep its original owner, bracket and independent deadlines.
bool labPartialExit = strategy.closedtrades > labPreviousClosed and f_camSign(strategy.position_size) != 0
if strategy.closedtrades > labPreviousClosed
for trade = labPreviousClosed to strategy.closedtrades - 1
if strategy.closedtrades.exit_comment(trade) == "Margin call"
labMarginExits += 1
// This also handles an entry and complete exit between calculations.
if f_camSign(strategy.position_size) == 0
labLastExitBar := bar_index
labTrackedCode := ""
labTrackedSession := na
labTrackedEpisode := na
labSignalBar := na
labStop := na
labTarget := na
labExpiryBar := na
labSessionEnd := na
labStatus := "Broker position fully closed · see trade list"
labPreviousClosed := strategy.closedtrades
bool labQueued = labTrackedCode != "" and f_camSign(strategy.position_size) == 0 and not na(labSignalBar)
if labQueued and bar_index > labSignalBar
// Market entries normally fill at the next available open. If the broker
// rejected it (margin/quantity), do not silently carry an orphan bracket.
strategy.cancel_all()
labTrackedCode := ""
labLastExitBar := bar_index
labStatus := "No broker fill · inspect quantity / margin"
labSkipped += 1
if f_camSign(strategy.position_size) != 0
labStatus := labTrackedCode + " broker " + (f_camSign(strategy.position_size) > 0 ? "LONG" : "SHORT") + " @ " + str.tostring(strategy.position_avg_price, format.mintick) + (labPartialExit ? " · partial exit; bracket retained" : "")
strategy.exit("Lab bracket", "Lab", stop = labStop, limit = labTarget, comment_loss = "Frozen stop", comment_profit = "Frozen target")
bool labTimeExit = time_close >= labTo or (not na(labExpiryBar) and bar_index >= labExpiryBar) or (not na(labSessionEnd) and time_close >= labSessionEnd)
if array.size(camBarEvents) > 0
for i = 0 to array.size(camBarEvents) - 1
CamEvent e = array.get(camBarEvents, i)
if e.code == labTrackedCode and e.sessionStart == labTrackedSession and e.episode == labTrackedEpisode and e.kind == "EXPIRED"
labTimeExit := true
if labTimeExit
// Default market close is next available tick, including overnight gaps.
strategy.close("Lab", comment = "Time / session expiry")
labStatus := labTrackedCode + " · time exit queued"
bool labFlat = f_camSign(strategy.position_size) == 0 and labTrackedCode == "" and (na(labLastExitBar) or bar_index > labLastExitBar)
bool labWindow = time_close >= labFrom and time_close < labTo
if labFlat and labWindow and array.size(camBarEvents) > 0
bool labSelected = false
for i = 0 to array.size(camBarEvents) - 1
CamEvent e = array.get(camBarEvents, i)
bool selectedCode = labCode == "All · fixed code order" or labCode == e.code
bool selectedSide = labSide == "Both" or (labSide == "Long only" and e.dir == 1) or (labSide == "Short only" and e.dir == -1)
if not labSelected and e.kind == "ACTIVE" and selectedCode and selectedSide
labSelected := true
labSignals += 1
float step = f_camSign(labQtyStep) > 0 ? labQtyStep : syminfo.mincontract
float unitRisk = strategy.convert_to_account(math.abs(e.entry - e.stop) * syminfo.pointvalue)
float unitValue = strategy.convert_to_account(e.entry * syminfo.pointvalue)
float quantity = f_labQuantity(strategy.equity, labRiskPct, labExposurePct, unitRisk, unitValue, step)
if f_camSign(quantity) > 0 and not na(e.stop) and not na(e.target)
labTrackedCode := e.code
labTrackedSession := e.sessionStart
labTrackedEpisode := e.episode
labSignalBar := bar_index
labExpiryBar := bar_index + activeDeadline
labSessionEnd := camSessionEnd
labStop := e.stop
labTarget := e.target
strategy.entry("Lab", e.dir == 1 ? strategy.long : strategy.short, qty = quantity, comment = e.code)
strategy.exit("Lab bracket", "Lab", stop = labStop, limit = labTarget, comment_loss = "Frozen stop", comment_profit = "Frozen target")
labStatus := e.code + " · entry queued for next tick"
else
labSkipped += 1
labStatus := e.code + " skipped · quantity / conversion"
if (barstate.islastconfirmedhistory or barstate.isrealtime) and labShow
table.cell(labPanel, 0, 0, "STRATEGY LAB · " + str.tostring(strategy.closedtrades) + " exits", text_color = vAccent, bgcolor = vHeader, text_size = vBodySize, text_halign = text.align_left, tooltip = "Broker closed records include partial exits. Margin-call records: " + str.tostring(labMarginExits) + ". A remaining position retains its original bracket and deadlines.")
table.cell(labPanel, 0, 1, f_vWrap(labStatus, 34, 2), text_color = vText, bgcolor = vBg, text_size = vBodySize, text_halign = text.align_left)
table.cell(labPanel, 0, 2, "Net " + str.tostring(strategy.netprofit, "#.##") + " · Max DD " + str.tostring(strategy.max_drawdown, "#.##"), text_color = vMuted, bgcolor = vBg, text_size = vSmallSize, text_halign = text.align_left, tooltip = "Account-currency amounts from the broker emulator; closed net profit and maximum equity drawdown. Consult Strategy Report for period, open P/L and trade details. The chart HUD shows structural references, not broker fill prices.")
string labCoverage = na(labFirstReady) ? "No signal-ready source coverage" : "Ready " + str.format_time(labFirstReady, "dd MMM", "Etc/UTC") + "–" + str.format_time(labLastReady, "dd MMM", "Etc/UTC") + " · " + str.tostring(labReadyBars) + " bars"
table.cell(labPanel, 0, 3, labCoverage, text_color = vMuted, bgcolor = vBg, text_size = vSmallSize, text_halign = text.align_left, tooltip = "Coverage includes valid contained chart bars with source and opening ready inside the selected test window, not necessarily a candidate. First/last dates do not imply uninterrupted coverage. Risk budget " + str.tostring(labRiskPct) + "%; skipped quantities/fills " + str.tostring(labSkipped) + ". Example defaults: 0.05% commission per side and 1 tick slippage. Actual costs are in Properties. Gaps and costs can exceed signal-price risk.")
table.cell(labPanel, 0, 4, "Emulated · verify costs in Properties", text_color = vMuted, bgcolor = vBg, text_size = vSmallSize, text_halign = text.align_left)
Source identity manifest · MIT licence. Save the two scripts separately; adding the indicator does not run the Lab.
Strategy Lab separately emulates selected activations, occupancy, next-available fills, frozen brackets, sizing and costs. A HUD reference is not its fill. Verify settings and source-ready dates first.
Read each opening gate, setup, confirming close and ending.
Open the question you are working on. All original rules, experiments and settings remain here.
Map, opening and completed ATR freeze for the decision session.
H6, H4, H3, PC, L3, L4 and L6 have solid lines and right-side price tags. PC is the selected prior close. H/L also means R/S; H2/L2 are not floor-pivot CPR.
Higher means selected close exceeds its predecessor by more than two percent of frozen ATR. Lower mirrors it. Equality is Neutral; Neutral defaults off.
Compare H3−L3 widths. Ratios 0.98–1.02 are Similar, inclusive. A/E require Wide or Similar; B/C/D Narrow or Similar; F/G any valid width. Band overlap is separate context.
D = H − L; PC = selected C. H2/L2 = C ± 1.1D/6; H3/L3 = C ± 1.1D/4; H4/L4 = C ± 1.1D/2; H6 = (H/L)C; L6 = 2C − H6. H5 = H4 + 1.168(H4 − H3), with L5 mirrored, is audit-only.
Original sources must be positive and coherent; an independent close may lie outside chosen H/L. A needs valid inner geometry; other families also require outer levels.
Crypto uses completed Exchange daily data. Non-crypto uses configured RTH, initially 09:30–16:00 New York. A futures provider daily bar is never relabelled cash RTH.
Crypto follows Exchange day. Others use US cash. RTH/ETH intervals, custom sessions and IANA timezones are explicit; configure other exchanges deliberately.
H/L and close can use separate source pairs. ATR has its own basis and timestamp, independent of pivot selection.
OPEN UNKNOWN means wait. One-minute opening and chart boundary coverage must be known; loading mid-session cannot certify missing bad-OPEN history.
ATR Market default selects observed RTH-session ATR(14) for futures unless the decision clock is Exchange day; Provider daily ATR(14) otherwise. Explicit selections override. RTH true ranges use complete sessions, not chart candles.
Fourteen complete observed RTH sessions seed the mean true range, then Wilder smoothing updates it. An observed incomplete or malformed session resets the chain; entirely absent dates are skipped. Missing, nonpositive or more than seven elapsed days old means ATR WAIT without fallback. Basis, value, accepted timestamp, chain start and sample count freeze at decision opening. This aggregation is not verified Premium private-formula parity.
Auto requires complete aligned pre/postmarket windows, coherent source pairs and ATR. Excursion strictly beyond the ATR tolerance chooses ETH; equality chooses RTH. Missing coverage blocks it. There is no silent fallback.
Market default needs no extended-hours data. Auto freezes at decision opening.
RTH/ETH/Selected session require observed one-minute boundaries, continuous coverage and valid OHLC. The 100,000 minutes requested cover about 69 continuous crypto days; visible chart history can extend farther.
Source pairs must be ordered, completed by decision opening and at most seven elapsed days old. Incomplete windows occupy the slot; absent windows are not classified as holidays. Coarse charts can use a known minute opening after an observed straddled boundary; missing chart coverage blocks.
Malformed OHLC clears previous-close knowledge; clock expiry continues. Overnight weekdays follow the ending date, full local days the starting date. Repeated/skipped DST endpoints remain unverified.
H/L names structure, not direction. O is frozen opening; C is latest committed close. Opening intervals include equality. Approach intervals are strict. A/E accept Wide or Similar, B/C/D Narrow or Similar, F/G any valid width.
| PLAY / CONTEXT / SIDE | OPENING GATE | APPROACH REGION | ORIGINAL SETUP → CONFIRMATION | STRUCTURAL DESTINATION |
|---|---|---|---|---|
| HA · L3 bounceHigher · long | L2 ≤ O ≤ H2 | L4 < C < PC | Touch L3; close above. | H3 |
| LA · H3 fadeLower · short | L2 ≤ O ≤ H2 | PC < C < H4 | Touch H3; close below. | L3 |
| HB · H4 break + retestHigher · long | L2 ≤ O ≤ H3 | H3 < C < H6 | Closed break above H4; later containing retest and close above. | H6 |
| LB · L4 break + retestLower · short | L3 ≤ O ≤ H2 | L6 < C < L3 | Closed break below L4; later containing retest and close below. | L6 |
| HC · H4 continuationHigher · long | H4 ≤ O ≤ H6 | C > H3 | Approach arms; touch H4 and close above. | H6 |
| LC · L4 continuationLower · short | L6 ≤ O ≤ L4 | C < L3 | Approach arms; touch L4 and close below. | L6 |
| HD · L4 counterHigher · short | L6 ≤ O ≤ L4 | C < L3 | Approach arms; touch L4 and close below. | L6 |
| LD · H4 counterLower · long | H4 ≤ O ≤ H6 | C > H3 | Approach arms; touch H4 and close above. | H6 |
| HE · H4 failureHigher · short | L2 ≤ O ≤ H3 | H3 < C < H6 | Close above H4 arms; later close below confirms failure. | L4 |
| LE · L4 failureLower · long | L3 ≤ O ≤ H2 | L6 < C < L3 | Close below L4 arms; later close above confirms failure. | H4 |
| HF · H6 rejectionHigher · short | H4 ≤ O ≤ H6 | C > H4 | Touch H6; close below. | Wide: H4; otherwise PC |
| LF · L6 rejectionLower · long | L6 ≤ O ≤ L4 | C < L4 | Touch L6; close above. | Wide: L4; otherwise PC |
| HG · L6 rejectionHigher · long · optional | L6 ≤ O ≤ PC | C < L4 | Touch L6; close above. G defaults off. | Wide: L4; otherwise PC |
| LG · H6 rejectionLower · short · optional | PC ≤ O ≤ H6 | C > H4 | Touch H6; close below. G defaults off. | Wide: H4; otherwise PC |
A touch means low ≤ tier ≤ high. A wrong-side gap is not a touch. Previous contained close starts at the known opening. B requires a strict closed crossing, with previous equality allowed.
Check every valid contained candle OPEN, not wick. Strictly outside these limits blocks the code until a new session: HA [L4,H2]; LA [L2,H4]; HB/HE [L2,H6]; LB/LE [L6,H2]; HC/LD/HF [H3,U]; LC/HD/LF [D,L3]; HG [D,PC]; LG [PC,U]. U = H6 + 0.5(H6−H4), D = L6 − 0.5(L4−L6). Equality does not block.
A blocked pending setup invalidates. ACTIVE retains its ending rules; recovery cannot clear the block.
A/F/G can touch and recover on the setup candle. Wrong-side touches need a later recovery from the previous wrong-side close. C/D can arm and confirm on a containing approach candle. B/E need a later bar. Confirmation still needs approach region, optional SMA alignment and acceptable geometry.
Flags describe prerequisites, not ranking. Multiple hypotheses, including opposite directions, may coexist.
Cancellation precedes confirmation. Pending A long cancels at close ≤ L4; short at ≥ H4. B/C/D long at ≤ H3; short at ≥ L3. E long at ≤ L6; short at ≥ H6. F/G have no additional pivot cancellation. A bad OPEN invalidates pending setups.
Setup confirmation is allowed through age twelve; age thirteen expires first. Price outcomes precede active expiry at age 120 and known session end. Final session close cannot start ACTIVE. Rollover expires an unfinished episode without inventing a fill.
Factory: one episode per code per session, consumed when armed, including failures. Optional rearming requires cooldown, remaining budget and valid unblocked prerequisites. Cooldown three permits reset at end-bar plus three, never on the ending bar. Rearming cannot clear a bad-OPEN latch.
ENTRY REF is the confirming close; direction, stop, target, time, source and settings freeze with it. R measures distances, not fills or returns.
A stop is beyond the farther of fourth tier and armed extreme. B/C/D use H3 for longs or L3 for shorts. E extends beyond outer tier and armed extreme. F/G extend beyond both armed extreme and one-quarter of fourth-to-third spacing outside the sixth tier.
The outward buffer defaults to one tick. Long stops round down, short stops up; targets round toward the activation close. Entry retains sub-tick precision. Prices must be positive, direction correct, risk at least one tick and ratio at least 1R after rounding.
Stop and target can independently use configurable multiples of frozen ATR. Optional maximum target distance rejects geometry without moving its destination. The lesson uses structural defaults; SMA and ATR alternatives are not simulated.
No activation-bar outcome. Later stop gaps use observed open; target gaps use fixed target. Otherwise stop touch precedes target touch. If both touch, ambiguity is marked instead of inferring an intrabar path.
Invalid or uncontained OHLC cannot settle price outcomes; clock expiry continues. Ended snapshots retain references and end time as LAST REFERENCE. Current distance uses last closed price; it does not move the plan.
Market default source and ATR basis; Auto clock; matching close. Display: Ribbon, Top Left, Trading Dark, seven levels with right tags; 12% header clearance. Range threshold and Similar tolerance both two percent. A–F on, G off, Neutral off. Structural stop and target, one-tick buffer, minimum 1R, setup twelve, active 120, SMA off, rearming off, one episode.
Exact input names and factory defaults from the current source. The explanations above show how they fit together. A disabled option may require its parent switch.
Market defaultAuto0930-1600America/New_Yorktrue0930-1600America/New_YorkAuto1800-1700America/New_YorkMatch source2Market default22falsefalse20truetruetruetruetruetruefalse121201.01Structural0.25Structural0.50false31truetruefalseRibbonOffTrading DarkMapMediumAutofalseTop LeftBottom Left12truefalsetrue212Righttrue14All · fixed code orderBoth0.590001 Jan 2020 00:00 +000001 Jan 2099 00:00 +0000trueTwo alert conditions cover activation and episode ending. One “Any alert() function call” alert aggregates selected same-bar events into JSON. Armed events default off.
In TradingView’s alert dialog the two conditions are named “Camarilla play activated” and “Camarilla episode ended”. The envelope from “Any alert() function call” opens with {"schema":"camarilla-desk.v2","product_version":"2.1.0","rule_version":"lite2-prerequisites.original2.1-execution", …} and lists each event’s code, direction, time and fixed references.
The envelope includes settings, symbol, timeframe, source/session and frozen ATR provenance, with no confirmed fill. Recreate alerts after code, input, symbol or timeframe changes. Indicator: named condition or “Any alert() function call”. Strategy Lab: alert() calls for structural JSON; order fills are a separate broker event stream. No alerts are created here.
Map and plan freeze at decision opening and activation respectively. Forming bars do not commit changes. Input/history changes recalculate; display changes only presentation.
H names Higher context; SIDE names direction. HE/HF are short hypotheses, while HD is opposed continuation.
Wrong-side destination, unusable risk or insufficient rounded ratio rejects confirmation and consumes the episode. It does not chase another close.
What shipped and what changed in Camarilla Desk, newest first. Ask for the next change below; what gets built lands here with a date, and on the lab queue on the front page.
Seven levels, clearer prerequisites and ATR v2.1.0 Updated Camarilla Desk
The map, ATR basis, fourteen play rules and separate Strategy Lab now match the current release.
Camarilla Desk v2.1.0 Shipped
Builds a session map, then checks a potential trade.
A small friction or a big idea. Tell us what would make this tool work better for you.
Requests for a new tool go on the same queue: see what is asked for and what is being built.
FREE & OPEN SOURCE · CLOSED-BAR RULES · REFERENCE LEVELS